• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.apf;
18 
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 
22 /**
23  * APF assembler/generator.  A tool for generating an APF program.
24  *
25  * Call add*() functions to add instructions to the program, then call
26  * {@link generate} to get the APF bytecode for the program.
27  *
28  * @hide
29  */
30 public class ApfGenerator {
31     /**
32      * This exception is thrown when an attempt is made to generate an illegal instruction.
33      */
34     public static class IllegalInstructionException extends Exception {
IllegalInstructionException(String msg)35         IllegalInstructionException(String msg) {
36             super(msg);
37         }
38     }
39     private enum Opcodes {
40         LABEL(-1),
41         LDB(1),    // Load 1 byte from immediate offset, e.g. "ldb R0, [5]"
42         LDH(2),    // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]"
43         LDW(3),    // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]"
44         LDBX(4),   // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0"
45         LDHX(5),   // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0"
46         LDWX(6),   // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0"
47         ADD(7),    // Add, e.g. "add R0,5"
48         MUL(8),    // Multiply, e.g. "mul R0,5"
49         DIV(9),    // Divide, e.g. "div R0,5"
50         AND(10),   // And, e.g. "and R0,5"
51         OR(11),    // Or, e.g. "or R0,5"
52         SH(12),    // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right)
53         LI(13),    // Load immediate, e.g. "li R0,5" (immediate encoded as signed value)
54         JMP(14),   // Jump, e.g. "jmp label"
55         JEQ(15),   // Compare equal and branch, e.g. "jeq R0,5,label"
56         JNE(16),   // Compare not equal and branch, e.g. "jne R0,5,label"
57         JGT(17),   // Compare greater than and branch, e.g. "jgt R0,5,label"
58         JLT(18),   // Compare less than and branch, e.g. "jlt R0,5,label"
59         JSET(19),  // Compare any bits set and branch, e.g. "jset R0,5,label"
60         JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
61         EXT(21);   // Followed by immediate indicating ExtendedOpcodes.
62 
63         final int value;
64 
Opcodes(int value)65         private Opcodes(int value) {
66             this.value = value;
67         }
68     }
69     // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate
70     // field.
71     private enum ExtendedOpcodes {
72         LDM(0),   // Load from memory, e.g. "ldm R0,5"
73         STM(16),  // Store to memory, e.g. "stm R0,5"
74         NOT(32),  // Not, e.g. "not R0"
75         NEG(33),  // Negate, e.g. "neg R0"
76         SWAP(34), // Swap, e.g. "swap R0,R1"
77         MOVE(35);  // Move, e.g. "move R0,R1"
78 
79         final int value;
80 
ExtendedOpcodes(int value)81         private ExtendedOpcodes(int value) {
82             this.value = value;
83         }
84     }
85     public enum Register {
86         R0(0),
87         R1(1);
88 
89         final int value;
90 
Register(int value)91         private Register(int value) {
92             this.value = value;
93         }
94     }
95     private class Instruction {
96         private final byte mOpcode;   // A "Opcode" value.
97         private final byte mRegister; // A "Register" value.
98         private boolean mHasImm;
99         private byte mImmSize;
100         private boolean mImmSigned;
101         private int mImm;
102         // When mOpcode is a jump:
103         private byte mTargetLabelSize;
104         private String mTargetLabel;
105         // When mOpcode == Opcodes.LABEL:
106         private String mLabel;
107         // When mOpcode == Opcodes.JNEBS:
108         private byte[] mCompareBytes;
109         // Offset in bytes from the begining of this program. Set by {@link ApfGenerator#generate}.
110         int offset;
111 
Instruction(Opcodes opcode, Register register)112         Instruction(Opcodes opcode, Register register) {
113             mOpcode = (byte)opcode.value;
114             mRegister = (byte)register.value;
115         }
116 
Instruction(Opcodes opcode)117         Instruction(Opcodes opcode) {
118             this(opcode, Register.R0);
119         }
120 
setImm(int imm, boolean signed)121         void setImm(int imm, boolean signed) {
122             mHasImm = true;
123             mImm = imm;
124             mImmSigned = signed;
125             mImmSize = calculateImmSize(imm, signed);
126         }
127 
setUnsignedImm(int imm)128         void setUnsignedImm(int imm) {
129             setImm(imm, false);
130         }
131 
setSignedImm(int imm)132         void setSignedImm(int imm) {
133             setImm(imm, true);
134         }
135 
setLabel(String label)136         void setLabel(String label) throws IllegalInstructionException {
137             if (mLabels.containsKey(label)) {
138                 throw new IllegalInstructionException("duplicate label " + label);
139             }
140             if (mOpcode != Opcodes.LABEL.value) {
141                 throw new IllegalStateException("adding label to non-label instruction");
142             }
143             mLabel = label;
144             mLabels.put(label, this);
145         }
146 
setTargetLabel(String label)147         void setTargetLabel(String label) {
148             mTargetLabel = label;
149             mTargetLabelSize = 4; // May shrink later on in generate().
150         }
151 
setCompareBytes(byte[] bytes)152         void setCompareBytes(byte[] bytes) {
153             if (mOpcode != Opcodes.JNEBS.value) {
154                 throw new IllegalStateException("adding compare bytes to non-JNEBS instruction");
155             }
156             mCompareBytes = bytes;
157         }
158 
159         /**
160          * @return size of instruction in bytes.
161          */
size()162         int size() {
163             if (mOpcode == Opcodes.LABEL.value) {
164                 return 0;
165             }
166             int size = 1;
167             if (mHasImm) {
168                 size += generatedImmSize();
169             }
170             if (mTargetLabel != null) {
171                 size += generatedImmSize();
172             }
173             if (mCompareBytes != null) {
174                 size += mCompareBytes.length;
175             }
176             return size;
177         }
178 
179         /**
180          * Resize immediate value field so that it's only as big as required to
181          * contain the offset of the jump destination.
182          * @return {@code true} if shrunk.
183          */
shrink()184         boolean shrink() throws IllegalInstructionException {
185             if (mTargetLabel == null) {
186                 return false;
187             }
188             int oldSize = size();
189             int oldTargetLabelSize = mTargetLabelSize;
190             mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false);
191             if (mTargetLabelSize > oldTargetLabelSize) {
192                 throw new IllegalStateException("instruction grew");
193             }
194             return size() < oldSize;
195         }
196 
197         /**
198          * Assemble value for instruction size field.
199          */
generateImmSizeField()200         private byte generateImmSizeField() {
201             byte immSize = generatedImmSize();
202             // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4.
203             return immSize == 4 ? 3 : immSize;
204         }
205 
206         /**
207          * Assemble first byte of generated instruction.
208          */
generateInstructionByte()209         private byte generateInstructionByte() {
210             byte sizeField = generateImmSizeField();
211             return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister);
212         }
213 
214         /**
215          * Write {@code value} at offset {@code writingOffset} into {@code bytecode}.
216          * {@link generatedImmSize} bytes are written. {@code value} is truncated to
217          * {@code generatedImmSize} bytes. {@code value} is treated simply as a
218          * 32-bit value, so unsigned values should be zero extended and the truncation
219          * should simply throw away their zero-ed upper bits, and signed values should
220          * be sign extended and the truncation should simply throw away their signed
221          * upper bits.
222          */
writeValue(int value, byte[] bytecode, int writingOffset)223         private int writeValue(int value, byte[] bytecode, int writingOffset) {
224             for (int i = generatedImmSize() - 1; i >= 0; i--) {
225                 bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255);
226             }
227             return writingOffset;
228         }
229 
230         /**
231          * Generate bytecode for this instruction at offset {@link offset}.
232          */
generate(byte[] bytecode)233         void generate(byte[] bytecode) throws IllegalInstructionException {
234             if (mOpcode == Opcodes.LABEL.value) {
235                 return;
236             }
237             int writingOffset = offset;
238             bytecode[writingOffset++] = generateInstructionByte();
239             if (mTargetLabel != null) {
240                 writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset);
241             }
242             if (mHasImm) {
243                 writingOffset = writeValue(mImm, bytecode, writingOffset);
244             }
245             if (mCompareBytes != null) {
246                 System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length);
247                 writingOffset += mCompareBytes.length;
248             }
249             if ((writingOffset - offset) != size()) {
250                 throw new IllegalStateException("wrote " + (writingOffset - offset) +
251                         " but should have written " + size());
252             }
253         }
254 
255         /**
256          * Calculate the size of either the immediate field or the target label field, if either is
257          * present. Most instructions have either an immediate or a target label field, but for the
258          * instructions that have both, the size of the target label field must be the same as the
259          * size of the immediate field, because there is only one length field in the instruction
260          * byte, hence why this function simply takes the maximum of the two sizes, so neither is
261          * truncated.
262          */
generatedImmSize()263         private byte generatedImmSize() {
264             return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize;
265         }
266 
calculateTargetLabelOffset()267         private int calculateTargetLabelOffset() throws IllegalInstructionException {
268             Instruction targetLabelInstruction;
269             if (mTargetLabel == DROP_LABEL) {
270                 targetLabelInstruction = mDropLabel;
271             } else if (mTargetLabel == PASS_LABEL) {
272                 targetLabelInstruction = mPassLabel;
273             } else {
274                 targetLabelInstruction = mLabels.get(mTargetLabel);
275             }
276             if (targetLabelInstruction == null) {
277                 throw new IllegalInstructionException("label not found: " + mTargetLabel);
278             }
279             // Calculate distance from end of this instruction to instruction.offset.
280             final int targetLabelOffset = targetLabelInstruction.offset - (offset + size());
281             if (targetLabelOffset < 0) {
282                 throw new IllegalInstructionException("backward branches disallowed; label: " +
283                         mTargetLabel);
284             }
285             return targetLabelOffset;
286         }
287 
calculateImmSize(int imm, boolean signed)288         private byte calculateImmSize(int imm, boolean signed) {
289             if (imm == 0) {
290                 return 0;
291             }
292             if (signed && (imm >= -128 && imm <= 127) ||
293                     !signed && (imm >= 0 && imm <= 255)) {
294                 return 1;
295             }
296             if (signed && (imm >= -32768 && imm <= 32767) ||
297                     !signed && (imm >= 0 && imm <= 65535)) {
298                 return 2;
299             }
300             return 4;
301         }
302     }
303 
304     /**
305      * Jump to this label to terminate the program and indicate the packet
306      * should be dropped.
307      */
308     public static final String DROP_LABEL = "__DROP__";
309 
310     /**
311      * Jump to this label to terminate the program and indicate the packet
312      * should be passed to the AP.
313      */
314     public static final String PASS_LABEL = "__PASS__";
315 
316     /**
317      * Number of memory slots available for access via APF stores to memory and loads from memory.
318      * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with
319      * the APF interpreter.
320      */
321     public static final int MEMORY_SLOTS = 16;
322 
323     /**
324      * Memory slot number that is prefilled with the IPv4 header length.
325      * Note that this memory slot may be overwritten by a program that
326      * executes stores to this memory slot. This must be kept in sync with
327      * the APF interpreter.
328      */
329     public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13;
330 
331     /**
332      * Memory slot number that is prefilled with the size of the packet being filtered in bytes.
333      * Note that this memory slot may be overwritten by a program that
334      * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
335      */
336     public static final int PACKET_SIZE_MEMORY_SLOT = 14;
337 
338     /**
339      * Memory slot number that is prefilled with the age of the filter in seconds. The age of the
340      * filter is the time since the filter was installed until now.
341      * Note that this memory slot may be overwritten by a program that
342      * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
343      */
344     public static final int FILTER_AGE_MEMORY_SLOT = 15;
345 
346     /**
347      * First memory slot containing prefilled values. Can be used in range comparisons to determine
348      * if memory slot index is within prefilled slots.
349      */
350     public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT;
351 
352     /**
353      * Last memory slot containing prefilled values. Can be used in range comparisons to determine
354      * if memory slot index is within prefilled slots.
355      */
356     public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT;
357 
358     private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>();
359     private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>();
360     private final Instruction mDropLabel = new Instruction(Opcodes.LABEL);
361     private final Instruction mPassLabel = new Instruction(Opcodes.LABEL);
362     private boolean mGenerated;
363 
364     /**
365      * Set version of APF instruction set to generate instructions for. Returns {@code true}
366      * if generating for this version is supported, {@code false} otherwise.
367      */
setApfVersion(int version)368     public boolean setApfVersion(int version) {
369         // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
370         return version == 2;
371     }
372 
addInstruction(Instruction instruction)373     private void addInstruction(Instruction instruction) {
374         if (mGenerated) {
375             throw new IllegalStateException("Program already generated");
376         }
377         mInstructions.add(instruction);
378     }
379 
380     /**
381      * Define a label at the current end of the program. Jumps can jump to this label. Labels are
382      * their own separate instructions, though with size 0. This facilitates having labels with
383      * no corresponding code to execute, for example a label at the end of a program. For example
384      * an {@link ApfGenerator} might be passed to a function that adds a filter like so:
385      * <pre>
386      *   load from packet
387      *   compare loaded data, jump if not equal to "next_filter"
388      *   load from packet
389      *   compare loaded data, jump if not equal to "next_filter"
390      *   jump to drop label
391      *   define "next_filter" here
392      * </pre>
393      * In this case "next_filter" may not have any generated code associated with it.
394      */
defineLabel(String name)395     public ApfGenerator defineLabel(String name) throws IllegalInstructionException {
396         Instruction instruction = new Instruction(Opcodes.LABEL);
397         instruction.setLabel(name);
398         addInstruction(instruction);
399         return this;
400     }
401 
402     /**
403      * Add an unconditional jump instruction to the end of the program.
404      */
addJump(String target)405     public ApfGenerator addJump(String target) {
406         Instruction instruction = new Instruction(Opcodes.JMP);
407         instruction.setTargetLabel(target);
408         addInstruction(instruction);
409         return this;
410     }
411 
412     /**
413      * Add an instruction to the end of the program to load the byte at offset {@code offset}
414      * bytes from the begining of the packet into {@code register}.
415      */
addLoad8(Register register, int offset)416     public ApfGenerator addLoad8(Register register, int offset) {
417         Instruction instruction = new Instruction(Opcodes.LDB, register);
418         instruction.setUnsignedImm(offset);
419         addInstruction(instruction);
420         return this;
421     }
422 
423     /**
424      * Add an instruction to the end of the program to load 16-bits at offset {@code offset}
425      * bytes from the begining of the packet into {@code register}.
426      */
addLoad16(Register register, int offset)427     public ApfGenerator addLoad16(Register register, int offset) {
428         Instruction instruction = new Instruction(Opcodes.LDH, register);
429         instruction.setUnsignedImm(offset);
430         addInstruction(instruction);
431         return this;
432     }
433 
434     /**
435      * Add an instruction to the end of the program to load 32-bits at offset {@code offset}
436      * bytes from the begining of the packet into {@code register}.
437      */
addLoad32(Register register, int offset)438     public ApfGenerator addLoad32(Register register, int offset) {
439         Instruction instruction = new Instruction(Opcodes.LDW, register);
440         instruction.setUnsignedImm(offset);
441         addInstruction(instruction);
442         return this;
443     }
444 
445     /**
446      * Add an instruction to the end of the program to load a byte from the packet into
447      * {@code register}. The offset of the loaded byte from the begining of the packet is
448      * the sum of {@code offset} and the value in register R1.
449      */
addLoad8Indexed(Register register, int offset)450     public ApfGenerator addLoad8Indexed(Register register, int offset) {
451         Instruction instruction = new Instruction(Opcodes.LDBX, register);
452         instruction.setUnsignedImm(offset);
453         addInstruction(instruction);
454         return this;
455     }
456 
457     /**
458      * Add an instruction to the end of the program to load 16-bits from the packet into
459      * {@code register}. The offset of the loaded 16-bits from the begining of the packet is
460      * the sum of {@code offset} and the value in register R1.
461      */
addLoad16Indexed(Register register, int offset)462     public ApfGenerator addLoad16Indexed(Register register, int offset) {
463         Instruction instruction = new Instruction(Opcodes.LDHX, register);
464         instruction.setUnsignedImm(offset);
465         addInstruction(instruction);
466         return this;
467     }
468 
469     /**
470      * Add an instruction to the end of the program to load 32-bits from the packet into
471      * {@code register}. The offset of the loaded 32-bits from the begining of the packet is
472      * the sum of {@code offset} and the value in register R1.
473      */
addLoad32Indexed(Register register, int offset)474     public ApfGenerator addLoad32Indexed(Register register, int offset) {
475         Instruction instruction = new Instruction(Opcodes.LDWX, register);
476         instruction.setUnsignedImm(offset);
477         addInstruction(instruction);
478         return this;
479     }
480 
481     /**
482      * Add an instruction to the end of the program to add {@code value} to register R0.
483      */
addAdd(int value)484     public ApfGenerator addAdd(int value) {
485         Instruction instruction = new Instruction(Opcodes.ADD);
486         instruction.setSignedImm(value);
487         addInstruction(instruction);
488         return this;
489     }
490 
491     /**
492      * Add an instruction to the end of the program to multiply register R0 by {@code value}.
493      */
addMul(int value)494     public ApfGenerator addMul(int value) {
495         Instruction instruction = new Instruction(Opcodes.MUL);
496         instruction.setSignedImm(value);
497         addInstruction(instruction);
498         return this;
499     }
500 
501     /**
502      * Add an instruction to the end of the program to divide register R0 by {@code value}.
503      */
addDiv(int value)504     public ApfGenerator addDiv(int value) {
505         Instruction instruction = new Instruction(Opcodes.DIV);
506         instruction.setSignedImm(value);
507         addInstruction(instruction);
508         return this;
509     }
510 
511     /**
512      * Add an instruction to the end of the program to logically and register R0 with {@code value}.
513      */
addAnd(int value)514     public ApfGenerator addAnd(int value) {
515         Instruction instruction = new Instruction(Opcodes.AND);
516         instruction.setUnsignedImm(value);
517         addInstruction(instruction);
518         return this;
519     }
520 
521     /**
522      * Add an instruction to the end of the program to logically or register R0 with {@code value}.
523      */
addOr(int value)524     public ApfGenerator addOr(int value) {
525         Instruction instruction = new Instruction(Opcodes.OR);
526         instruction.setUnsignedImm(value);
527         addInstruction(instruction);
528         return this;
529     }
530 
531     /**
532      * Add an instruction to the end of the program to shift left register R0 by {@code value} bits.
533      */
addLeftShift(int value)534     public ApfGenerator addLeftShift(int value) {
535         Instruction instruction = new Instruction(Opcodes.SH);
536         instruction.setSignedImm(value);
537         addInstruction(instruction);
538         return this;
539     }
540 
541     /**
542      * Add an instruction to the end of the program to shift right register R0 by {@code value}
543      * bits.
544      */
addRightShift(int value)545     public ApfGenerator addRightShift(int value) {
546         Instruction instruction = new Instruction(Opcodes.SH);
547         instruction.setSignedImm(-value);
548         addInstruction(instruction);
549         return this;
550     }
551 
552     /**
553      * Add an instruction to the end of the program to add register R1 to register R0.
554      */
addAddR1()555     public ApfGenerator addAddR1() {
556         Instruction instruction = new Instruction(Opcodes.ADD, Register.R1);
557         addInstruction(instruction);
558         return this;
559     }
560 
561     /**
562      * Add an instruction to the end of the program to multiply register R0 by register R1.
563      */
addMulR1()564     public ApfGenerator addMulR1() {
565         Instruction instruction = new Instruction(Opcodes.MUL, Register.R1);
566         addInstruction(instruction);
567         return this;
568     }
569 
570     /**
571      * Add an instruction to the end of the program to divide register R0 by register R1.
572      */
addDivR1()573     public ApfGenerator addDivR1() {
574         Instruction instruction = new Instruction(Opcodes.DIV, Register.R1);
575         addInstruction(instruction);
576         return this;
577     }
578 
579     /**
580      * Add an instruction to the end of the program to logically and register R0 with register R1
581      * and store the result back into register R0.
582      */
addAndR1()583     public ApfGenerator addAndR1() {
584         Instruction instruction = new Instruction(Opcodes.AND, Register.R1);
585         addInstruction(instruction);
586         return this;
587     }
588 
589     /**
590      * Add an instruction to the end of the program to logically or register R0 with register R1
591      * and store the result back into register R0.
592      */
addOrR1()593     public ApfGenerator addOrR1() {
594         Instruction instruction = new Instruction(Opcodes.OR, Register.R1);
595         addInstruction(instruction);
596         return this;
597     }
598 
599     /**
600      * Add an instruction to the end of the program to shift register R0 left by the value in
601      * register R1.
602      */
addLeftShiftR1()603     public ApfGenerator addLeftShiftR1() {
604         Instruction instruction = new Instruction(Opcodes.SH, Register.R1);
605         addInstruction(instruction);
606         return this;
607     }
608 
609     /**
610      * Add an instruction to the end of the program to move {@code value} into {@code register}.
611      */
addLoadImmediate(Register register, int value)612     public ApfGenerator addLoadImmediate(Register register, int value) {
613         Instruction instruction = new Instruction(Opcodes.LI, register);
614         instruction.setSignedImm(value);
615         addInstruction(instruction);
616         return this;
617     }
618 
619     /**
620      * Add an instruction to the end of the program to jump to {@code target} if register R0's
621      * value equals {@code value}.
622      */
addJumpIfR0Equals(int value, String target)623     public ApfGenerator addJumpIfR0Equals(int value, String target) {
624         Instruction instruction = new Instruction(Opcodes.JEQ);
625         instruction.setUnsignedImm(value);
626         instruction.setTargetLabel(target);
627         addInstruction(instruction);
628         return this;
629     }
630 
631     /**
632      * Add an instruction to the end of the program to jump to {@code target} if register R0's
633      * value does not equal {@code value}.
634      */
addJumpIfR0NotEquals(int value, String target)635     public ApfGenerator addJumpIfR0NotEquals(int value, String target) {
636         Instruction instruction = new Instruction(Opcodes.JNE);
637         instruction.setUnsignedImm(value);
638         instruction.setTargetLabel(target);
639         addInstruction(instruction);
640         return this;
641     }
642 
643     /**
644      * Add an instruction to the end of the program to jump to {@code target} if register R0's
645      * value is greater than {@code value}.
646      */
addJumpIfR0GreaterThan(int value, String target)647     public ApfGenerator addJumpIfR0GreaterThan(int value, String target) {
648         Instruction instruction = new Instruction(Opcodes.JGT);
649         instruction.setUnsignedImm(value);
650         instruction.setTargetLabel(target);
651         addInstruction(instruction);
652         return this;
653     }
654 
655     /**
656      * Add an instruction to the end of the program to jump to {@code target} if register R0's
657      * value is less than {@code value}.
658      */
addJumpIfR0LessThan(int value, String target)659     public ApfGenerator addJumpIfR0LessThan(int value, String target) {
660         Instruction instruction = new Instruction(Opcodes.JLT);
661         instruction.setUnsignedImm(value);
662         instruction.setTargetLabel(target);
663         addInstruction(instruction);
664         return this;
665     }
666 
667     /**
668      * Add an instruction to the end of the program to jump to {@code target} if register R0's
669      * value has any bits set that are also set in {@code value}.
670      */
addJumpIfR0AnyBitsSet(int value, String target)671     public ApfGenerator addJumpIfR0AnyBitsSet(int value, String target) {
672         Instruction instruction = new Instruction(Opcodes.JSET);
673         instruction.setUnsignedImm(value);
674         instruction.setTargetLabel(target);
675         addInstruction(instruction);
676         return this;
677     }
678     /**
679      * Add an instruction to the end of the program to jump to {@code target} if register R0's
680      * value equals register R1's value.
681      */
addJumpIfR0EqualsR1(String target)682     public ApfGenerator addJumpIfR0EqualsR1(String target) {
683         Instruction instruction = new Instruction(Opcodes.JEQ, Register.R1);
684         instruction.setTargetLabel(target);
685         addInstruction(instruction);
686         return this;
687     }
688 
689     /**
690      * Add an instruction to the end of the program to jump to {@code target} if register R0's
691      * value does not equal register R1's value.
692      */
addJumpIfR0NotEqualsR1(String target)693     public ApfGenerator addJumpIfR0NotEqualsR1(String target) {
694         Instruction instruction = new Instruction(Opcodes.JNE, Register.R1);
695         instruction.setTargetLabel(target);
696         addInstruction(instruction);
697         return this;
698     }
699 
700     /**
701      * Add an instruction to the end of the program to jump to {@code target} if register R0's
702      * value is greater than register R1's value.
703      */
addJumpIfR0GreaterThanR1(String target)704     public ApfGenerator addJumpIfR0GreaterThanR1(String target) {
705         Instruction instruction = new Instruction(Opcodes.JGT, Register.R1);
706         instruction.setTargetLabel(target);
707         addInstruction(instruction);
708         return this;
709     }
710 
711     /**
712      * Add an instruction to the end of the program to jump to {@code target} if register R0's
713      * value is less than register R1's value.
714      */
addJumpIfR0LessThanR1(String target)715     public ApfGenerator addJumpIfR0LessThanR1(String target) {
716         Instruction instruction = new Instruction(Opcodes.JLT, Register.R1);
717         instruction.setTargetLabel(target);
718         addInstruction(instruction);
719         return this;
720     }
721 
722     /**
723      * Add an instruction to the end of the program to jump to {@code target} if register R0's
724      * value has any bits set that are also set in R1's value.
725      */
addJumpIfR0AnyBitsSetR1(String target)726     public ApfGenerator addJumpIfR0AnyBitsSetR1(String target) {
727         Instruction instruction = new Instruction(Opcodes.JSET, Register.R1);
728         instruction.setTargetLabel(target);
729         addInstruction(instruction);
730         return this;
731     }
732 
733     /**
734      * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
735      * packet at, an offset specified by {@code register}, match {@code bytes}.
736      */
addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)737     public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
738             throws IllegalInstructionException {
739         if (register == Register.R1) {
740             throw new IllegalInstructionException("JNEBS fails with R1");
741         }
742         Instruction instruction = new Instruction(Opcodes.JNEBS, register);
743         instruction.setUnsignedImm(bytes.length);
744         instruction.setTargetLabel(target);
745         instruction.setCompareBytes(bytes);
746         addInstruction(instruction);
747         return this;
748     }
749 
750     /**
751      * Add an instruction to the end of the program to load memory slot {@code slot} into
752      * {@code register}.
753      */
addLoadFromMemory(Register register, int slot)754     public ApfGenerator addLoadFromMemory(Register register, int slot)
755             throws IllegalInstructionException {
756         if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
757             throw new IllegalInstructionException("illegal memory slot number: " + slot);
758         }
759         Instruction instruction = new Instruction(Opcodes.EXT, register);
760         instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot);
761         addInstruction(instruction);
762         return this;
763     }
764 
765     /**
766      * Add an instruction to the end of the program to store {@code register} into memory slot
767      * {@code slot}.
768      */
addStoreToMemory(Register register, int slot)769     public ApfGenerator addStoreToMemory(Register register, int slot)
770             throws IllegalInstructionException {
771         if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
772             throw new IllegalInstructionException("illegal memory slot number: " + slot);
773         }
774         Instruction instruction = new Instruction(Opcodes.EXT, register);
775         instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot);
776         addInstruction(instruction);
777         return this;
778     }
779 
780     /**
781      * Add an instruction to the end of the program to logically not {@code register}.
782      */
addNot(Register register)783     public ApfGenerator addNot(Register register) {
784         Instruction instruction = new Instruction(Opcodes.EXT, register);
785         instruction.setUnsignedImm(ExtendedOpcodes.NOT.value);
786         addInstruction(instruction);
787         return this;
788     }
789 
790     /**
791      * Add an instruction to the end of the program to negate {@code register}.
792      */
addNeg(Register register)793     public ApfGenerator addNeg(Register register) {
794         Instruction instruction = new Instruction(Opcodes.EXT, register);
795         instruction.setUnsignedImm(ExtendedOpcodes.NEG.value);
796         addInstruction(instruction);
797         return this;
798     }
799 
800     /**
801      * Add an instruction to swap the values in register R0 and register R1.
802      */
addSwap()803     public ApfGenerator addSwap() {
804         Instruction instruction = new Instruction(Opcodes.EXT);
805         instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value);
806         addInstruction(instruction);
807         return this;
808     }
809 
810     /**
811      * Add an instruction to the end of the program to move the value into
812      * {@code register} from the other register.
813      */
addMove(Register register)814     public ApfGenerator addMove(Register register) {
815         Instruction instruction = new Instruction(Opcodes.EXT, register);
816         instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value);
817         addInstruction(instruction);
818         return this;
819     }
820 
821     /**
822      * Updates instruction offset fields using latest instruction sizes.
823      * @return current program length in bytes.
824      */
updateInstructionOffsets()825     private int updateInstructionOffsets() {
826         int offset = 0;
827         for (Instruction instruction : mInstructions) {
828             instruction.offset = offset;
829             offset += instruction.size();
830         }
831         return offset;
832     }
833 
834     /**
835      * Returns an overestimate of the size of the generated program. {@link #generate} may return
836      * a program that is smaller.
837      */
programLengthOverEstimate()838     public int programLengthOverEstimate() {
839         return updateInstructionOffsets();
840     }
841 
842     /**
843      * Generate the bytecode for the APF program.
844      * @return the bytecode.
845      * @throws IllegalStateException if a label is referenced but not defined.
846      */
generate()847     public byte[] generate() throws IllegalInstructionException {
848         // Enforce that we can only generate once because we cannot unshrink instructions and
849         // PASS/DROP labels may move further away requiring unshrinking if we add further
850         // instructions.
851         if (mGenerated) {
852             throw new IllegalStateException("Can only generate() once!");
853         }
854         mGenerated = true;
855         int total_size;
856         boolean shrunk;
857         // Shrink the immediate value fields of instructions.
858         // As we shrink the instructions some branch offset
859         // fields may shrink also, thereby shrinking the
860         // instructions further. Loop until we've reached the
861         // minimum size. Rarely will this loop more than a few times.
862         // Limit iterations to avoid O(n^2) behavior.
863         int iterations_remaining = 10;
864         do {
865             total_size = updateInstructionOffsets();
866             // Update drop and pass label offsets.
867             mDropLabel.offset = total_size + 1;
868             mPassLabel.offset = total_size;
869             // Limit run-time in aberant circumstances.
870             if (iterations_remaining-- == 0) break;
871             // Attempt to shrink instructions.
872             shrunk = false;
873             for (Instruction instruction : mInstructions) {
874                 if (instruction.shrink()) {
875                     shrunk = true;
876                 }
877             }
878         } while (shrunk);
879         // Generate bytecode for instructions.
880         byte[] bytecode = new byte[total_size];
881         for (Instruction instruction : mInstructions) {
882             instruction.generate(bytecode);
883         }
884         return bytecode;
885     }
886 }
887 
888