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