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
17package android.net.apf;
18
19import java.util.ArrayList;
20import 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 */
30public 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 {
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
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
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
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 begining of this program. Set by {@link ApfGenerator#generate}.
112        int offset;
113
114        Instruction(Opcodes opcode, Register register) {
115            mOpcode = (byte)opcode.value;
116            mRegister = (byte)register.value;
117        }
118
119        Instruction(Opcodes opcode) {
120            this(opcode, Register.R0);
121        }
122
123        void setImm(int imm, boolean signed) {
124            mHasImm = true;
125            mImm = imm;
126            mImmSigned = signed;
127            mImmSize = calculateImmSize(imm, signed);
128        }
129
130        void setUnsignedImm(int imm) {
131            setImm(imm, false);
132        }
133
134        void setSignedImm(int imm) {
135            setImm(imm, true);
136        }
137
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
149        void setTargetLabel(String label) {
150            mTargetLabel = label;
151            mTargetLabelSize = 4; // May shrink later on in generate().
152        }
153
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         */
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         */
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         */
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         */
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         */
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         */
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         */
265        private byte generatedImmSize() {
266            return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize;
267        }
268
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
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     */
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     */
383    public static boolean supportsVersion(int version) {
384        return version >= MIN_APF_VERSION;
385    }
386
387    private void requireApfVersion(int minimumVersion) throws IllegalInstructionException {
388        if (mVersion < minimumVersion) {
389            throw new IllegalInstructionException("Requires APF >= " + minimumVersion);
390        }
391    }
392
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     */
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     */
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 begining of the packet into {@code register}.
435     */
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 begining of the packet into {@code register}.
446     */
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 begining of the packet into {@code register}.
457     */
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 begining of the packet is
468     * the sum of {@code offset} and the value in register R1.
469     */
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 begining of the packet is
480     * the sum of {@code offset} and the value in register R1.
481     */
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 begining of the packet is
492     * the sum of {@code offset} and the value in register R1.
493     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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} match {@code bytes}.
756     */
757    public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
758            throws IllegalInstructionException {
759        if (register == Register.R1) {
760            throw new IllegalInstructionException("JNEBS fails with R1");
761        }
762        Instruction instruction = new Instruction(Opcodes.JNEBS, register);
763        instruction.setUnsignedImm(bytes.length);
764        instruction.setTargetLabel(target);
765        instruction.setCompareBytes(bytes);
766        addInstruction(instruction);
767        return this;
768    }
769
770    /**
771     * Add an instruction to the end of the program to load memory slot {@code slot} into
772     * {@code register}.
773     */
774    public ApfGenerator addLoadFromMemory(Register register, int slot)
775            throws IllegalInstructionException {
776        if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
777            throw new IllegalInstructionException("illegal memory slot number: " + slot);
778        }
779        Instruction instruction = new Instruction(Opcodes.EXT, register);
780        instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot);
781        addInstruction(instruction);
782        return this;
783    }
784
785    /**
786     * Add an instruction to the end of the program to store {@code register} into memory slot
787     * {@code slot}.
788     */
789    public ApfGenerator addStoreToMemory(Register register, int slot)
790            throws IllegalInstructionException {
791        if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
792            throw new IllegalInstructionException("illegal memory slot number: " + slot);
793        }
794        Instruction instruction = new Instruction(Opcodes.EXT, register);
795        instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot);
796        addInstruction(instruction);
797        return this;
798    }
799
800    /**
801     * Add an instruction to the end of the program to logically not {@code register}.
802     */
803    public ApfGenerator addNot(Register register) {
804        Instruction instruction = new Instruction(Opcodes.EXT, register);
805        instruction.setUnsignedImm(ExtendedOpcodes.NOT.value);
806        addInstruction(instruction);
807        return this;
808    }
809
810    /**
811     * Add an instruction to the end of the program to negate {@code register}.
812     */
813    public ApfGenerator addNeg(Register register) {
814        Instruction instruction = new Instruction(Opcodes.EXT, register);
815        instruction.setUnsignedImm(ExtendedOpcodes.NEG.value);
816        addInstruction(instruction);
817        return this;
818    }
819
820    /**
821     * Add an instruction to swap the values in register R0 and register R1.
822     */
823    public ApfGenerator addSwap() {
824        Instruction instruction = new Instruction(Opcodes.EXT);
825        instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value);
826        addInstruction(instruction);
827        return this;
828    }
829
830    /**
831     * Add an instruction to the end of the program to move the value into
832     * {@code register} from the other register.
833     */
834    public ApfGenerator addMove(Register register) {
835        Instruction instruction = new Instruction(Opcodes.EXT, register);
836        instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value);
837        addInstruction(instruction);
838        return this;
839    }
840
841    /**
842     * Add an instruction to the end of the program to load 32 bits from the data memory into
843     * {@code register}. The source address is computed by adding the signed immediate
844     * @{code offset} to the other register.
845     * Requires APF v3 or greater.
846     */
847    public ApfGenerator addLoadData(Register destinationRegister, int offset)
848            throws IllegalInstructionException {
849        requireApfVersion(3);
850        Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
851        instruction.setSignedImm(offset);
852        addInstruction(instruction);
853        return this;
854    }
855
856    /**
857     * Add an instruction to the end of the program to store 32 bits from {@code register} into the
858     * data memory. The destination address is computed by adding the signed immediate
859     * @{code offset} to the other register.
860     * Requires APF v3 or greater.
861     */
862    public ApfGenerator addStoreData(Register sourceRegister, int offset)
863            throws IllegalInstructionException {
864        requireApfVersion(3);
865        Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
866        instruction.setSignedImm(offset);
867        addInstruction(instruction);
868        return this;
869    }
870
871    /**
872     * Updates instruction offset fields using latest instruction sizes.
873     * @return current program length in bytes.
874     */
875    private int updateInstructionOffsets() {
876        int offset = 0;
877        for (Instruction instruction : mInstructions) {
878            instruction.offset = offset;
879            offset += instruction.size();
880        }
881        return offset;
882    }
883
884    /**
885     * Returns an overestimate of the size of the generated program. {@link #generate} may return
886     * a program that is smaller.
887     */
888    public int programLengthOverEstimate() {
889        return updateInstructionOffsets();
890    }
891
892    /**
893     * Generate the bytecode for the APF program.
894     * @return the bytecode.
895     * @throws IllegalStateException if a label is referenced but not defined.
896     */
897    public byte[] generate() throws IllegalInstructionException {
898        // Enforce that we can only generate once because we cannot unshrink instructions and
899        // PASS/DROP labels may move further away requiring unshrinking if we add further
900        // instructions.
901        if (mGenerated) {
902            throw new IllegalStateException("Can only generate() once!");
903        }
904        mGenerated = true;
905        int total_size;
906        boolean shrunk;
907        // Shrink the immediate value fields of instructions.
908        // As we shrink the instructions some branch offset
909        // fields may shrink also, thereby shrinking the
910        // instructions further. Loop until we've reached the
911        // minimum size. Rarely will this loop more than a few times.
912        // Limit iterations to avoid O(n^2) behavior.
913        int iterations_remaining = 10;
914        do {
915            total_size = updateInstructionOffsets();
916            // Update drop and pass label offsets.
917            mDropLabel.offset = total_size + 1;
918            mPassLabel.offset = total_size;
919            // Limit run-time in aberant circumstances.
920            if (iterations_remaining-- == 0) break;
921            // Attempt to shrink instructions.
922            shrunk = false;
923            for (Instruction instruction : mInstructions) {
924                if (instruction.shrink()) {
925                    shrunk = true;
926                }
927            }
928        } while (shrunk);
929        // Generate bytecode for instructions.
930        byte[] bytecode = new byte[total_size];
931        for (Instruction instruction : mInstructions) {
932            instruction.generate(bytecode);
933        }
934        return bytecode;
935    }
936}
937
938