001/*
002 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.truffle.test;
024
025import java.util.*;
026
027import org.junit.*;
028
029import com.oracle.truffle.api.*;
030import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
031import com.oracle.truffle.api.frame.*;
032import com.oracle.truffle.api.nodes.*;
033
034public class BytecodeInterpreterPartialEvaluationTest extends PartialEvaluationTest {
035
036    public static class Bytecode {
037        public static final byte CONST = 0;
038        public static final byte RETURN = 1;
039        public static final byte ADD = 2;
040        public static final byte IFZERO = 3;
041        public static final byte POP = 4;
042        public static final byte JMP = 5;
043        public static final byte DUP = 6;
044    }
045
046    public static boolean TRACE = false;
047
048    public static class Program extends RootNode {
049        private final String name;
050        @CompilationFinal private final byte[] bytecodes;
051        @CompilationFinal private final FrameSlot[] locals;
052        @CompilationFinal private final FrameSlot[] stack;
053
054        public Program(String name, byte[] bytecodes, int maxLocals, int maxStack) {
055            super(MockLanguage.class, null, null);
056            this.name = name;
057            this.bytecodes = bytecodes;
058            locals = new FrameSlot[maxLocals];
059            stack = new FrameSlot[maxStack];
060            for (int i = 0; i < maxLocals; ++i) {
061                locals[i] = this.getFrameDescriptor().addFrameSlot("local" + i);
062                locals[i].setKind(FrameSlotKind.Int);
063            }
064            for (int i = 0; i < maxStack; ++i) {
065                stack[i] = this.getFrameDescriptor().addFrameSlot("stack" + i);
066                stack[i].setKind(FrameSlotKind.Int);
067            }
068        }
069
070        protected void setInt(VirtualFrame frame, int stackIndex, int value) {
071            frame.setInt(stack[stackIndex], value);
072        }
073
074        protected int getInt(VirtualFrame frame, int stackIndex) {
075            try {
076                return frame.getInt(stack[stackIndex]);
077            } catch (FrameSlotTypeException e) {
078                throw new IllegalStateException("Error accessing stack slot " + stackIndex);
079            }
080        }
081
082        @Override
083        public String toString() {
084            return name;
085        }
086
087        public void trace(String format, Object... args) {
088            if (CompilerDirectives.inInterpreter() && TRACE) {
089                System.out.println(String.format(format, args));
090            }
091        }
092
093        @Override
094        @ExplodeLoop(merge = true)
095        public Object execute(VirtualFrame frame) {
096            trace("Start program");
097            int topOfStack = -1;
098            int bci = 0;
099            while (true) {
100                CompilerAsserts.partialEvaluationConstant(bci);
101                byte bc = bytecodes[bci];
102                byte value = 0;
103                switch (bc) {
104                    case Bytecode.CONST:
105                        value = bytecodes[bci + 1];
106                        trace("%d (%d): CONST %s", bci, topOfStack, value);
107                        setInt(frame, ++topOfStack, value);
108                        bci = bci + 2;
109                        continue;
110
111                    case Bytecode.RETURN:
112                        trace("%d (%d): RETURN", bci, topOfStack);
113                        return getInt(frame, topOfStack);
114
115                    case Bytecode.ADD: {
116                        int left = getInt(frame, topOfStack);
117                        int right = getInt(frame, topOfStack - 1);
118                        trace("%d (%d): ADD %d %d", bci, topOfStack, left, right);
119                        setInt(frame, topOfStack - 1, left + right);
120                        topOfStack--;
121                        bci = bci + 1;
122                        continue;
123                    }
124
125                    case Bytecode.IFZERO:
126                        trace("%d (%d): IFZERO", bci, topOfStack);
127                        if (getInt(frame, topOfStack--) == 0) {
128                            bci = bytecodes[bci + 1];
129                            continue;
130                        } else {
131                            bci = bci + 2;
132                            continue;
133                        }
134
135                    case Bytecode.POP:
136                        trace("%d (%d): POP", bci, topOfStack);
137                        topOfStack--;
138                        bci++;
139                        continue;
140
141                    case Bytecode.JMP:
142                        trace("%d (%d): JMP", bci, topOfStack);
143                        bci = bytecodes[bci + 1];
144                        continue;
145
146                    case Bytecode.DUP:
147                        trace("%d (%d): DUP", bci, topOfStack);
148                        setInt(frame, topOfStack + 1, getInt(frame, topOfStack));
149                        topOfStack++;
150                        bci++;
151                        continue;
152                }
153            }
154        }
155    }
156
157    public static Object constant42() {
158        return 42;
159    }
160
161    private static void assertReturns42(RootNode program) {
162        Assert.assertEquals(Integer.valueOf(42), Truffle.getRuntime().createCallTarget(program).call());
163    }
164
165    private void assertPartialEvalEqualsAndRunsCorrect(RootNode program) {
166        assertReturns42(program);
167        assertPartialEvalEquals("constant42", program);
168    }
169
170    @Test
171    public void constReturnProgram() {
172        byte[] bytecodes = new byte[]{
173        /* 0: */Bytecode.CONST,
174        /* 1: */42,
175        /* 2: */Bytecode.RETURN};
176        assertPartialEvalEqualsAndRunsCorrect(new Program("constReturnProgram", bytecodes, 0, 2));
177    }
178
179    @Test
180    public void constAddProgram() {
181        byte[] bytecodes = new byte[]{
182        /* 0: */Bytecode.CONST,
183        /* 1: */40,
184        /* 2: */Bytecode.CONST,
185        /* 3: */2,
186        /* 4: */Bytecode.ADD,
187        /* 5: */Bytecode.RETURN};
188        assertPartialEvalEqualsAndRunsCorrect(new Program("constAddProgram", bytecodes, 0, 2));
189    }
190
191    @Test
192    public void simpleIfProgram() {
193        byte[] bytecodes = new byte[]{
194        /* 0: */Bytecode.CONST,
195        /* 1: */40,
196        /* 2: */Bytecode.CONST,
197        /* 3: */1,
198        /* 4: */Bytecode.IFZERO,
199        /* 5: */8,
200        /* 6: */Bytecode.CONST,
201        /* 7: */42,
202        /* 8: */Bytecode.RETURN};
203        assertPartialEvalEqualsAndRunsCorrect(new Program("simpleIfProgram", bytecodes, 0, 3));
204    }
205
206    @Test
207    public void ifAndPopProgram() {
208        byte[] bytecodes = new byte[]{
209        /* 0: */Bytecode.CONST,
210        /* 1: */40,
211        /* 2: */Bytecode.CONST,
212        /* 3: */1,
213        /* 4: */Bytecode.IFZERO,
214        /* 5: */9,
215        /* 6: */Bytecode.POP,
216        /* 7: */Bytecode.CONST,
217        /* 8: */42,
218        /* 9: */Bytecode.RETURN};
219        assertPartialEvalEqualsAndRunsCorrect(new Program("ifAndPopProgram", bytecodes, 0, 3));
220    }
221
222    @Test
223    public void simpleLoopProgram() {
224        byte[] bytecodes = new byte[]{
225        /* 0: */Bytecode.CONST,
226        /* 1: */42,
227        /* 2: */Bytecode.CONST,
228        /* 3: */-12,
229        /* 4: */Bytecode.CONST,
230        /* 5: */1,
231        /* 6: */Bytecode.ADD,
232        /* 7: */Bytecode.DUP,
233        /* 8: */Bytecode.IFZERO,
234        /* 9: */12,
235        /* 10: */Bytecode.JMP,
236        /* 11: */4,
237        /* 12: */Bytecode.POP,
238        /* 13: */Bytecode.RETURN};
239        assertPartialEvalEqualsAndRunsCorrect(new Program("simpleLoopProgram", bytecodes, 0, 3));
240    }
241
242    @Test
243    public void nestedLoopsProgram() {
244        byte[] bytecodes = new byte[]{
245        /* 0: */Bytecode.CONST,
246        /* 1: */42,
247        /* 2: */Bytecode.CONST,
248        /* 3: */-2,
249        /* 4: */Bytecode.CONST,
250        /* 5: */1,
251        /* 6: */Bytecode.ADD,
252        /* 7: */Bytecode.DUP,
253        /* 8: */Bytecode.CONST,
254        /* 9: */-2,
255        /* 10: */Bytecode.CONST,
256        /* 11: */1,
257        /* 12: */Bytecode.ADD,
258        /* 13: */Bytecode.DUP,
259        /* 14: */Bytecode.IFZERO,
260        /* 15: */18,
261        /* 16: */Bytecode.JMP,
262        /* 17: */10,
263        /* 18: */Bytecode.POP,
264        /* 19: */Bytecode.IFZERO,
265        /* 20: */23,
266        /* 21: */Bytecode.JMP,
267        /* 22: */4,
268        /* 23: */Bytecode.POP,
269        /* 24: */Bytecode.RETURN};
270        assertPartialEvalEqualsAndRunsCorrect(new Program("nestedLoopsProgram", bytecodes, 0, 6));
271    }
272
273    @Test(timeout = 2000)
274    public void manyIfsProgram() {
275        byte[] bytecodes = new byte[]{
276        /* 0: */Bytecode.CONST,
277        /* 1: */40,
278        /* 2: */Bytecode.CONST,
279        /* 3: */1,
280        /* 4: */Bytecode.IFZERO,
281        /* 5: */8,
282        /* 6: */Bytecode.CONST,
283        /* 7: */1,
284        /* 8: */Bytecode.IFZERO,
285        /* 9: */12,
286        /* 10: */Bytecode.CONST,
287        /* 11: */1,
288        /* 12: */Bytecode.IFZERO,
289        /* 13: */16,
290        /* 14: */Bytecode.CONST,
291        /* 15: */1,
292        /* 16: */Bytecode.IFZERO,
293        /* 17: */20,
294        /* 18: */Bytecode.CONST,
295        /* 19: */1,
296        /* 20: */Bytecode.IFZERO,
297        /* 21: */24,
298        /* 22: */Bytecode.CONST,
299        /* 23: */1,
300        /* 24: */Bytecode.IFZERO,
301        /* 25: */28,
302        /* 26: */Bytecode.CONST,
303        /* 27: */1,
304        /* 28: */Bytecode.IFZERO,
305        /* 29: */32,
306        /* 30: */Bytecode.CONST,
307        /* 31: */1,
308        /* 32: */Bytecode.IFZERO,
309        /* 33: */36,
310        /* 34: */Bytecode.CONST,
311        /* 35: */1,
312        /* 36: */Bytecode.IFZERO,
313        /* 37: */40,
314        /* 38: */Bytecode.CONST,
315        /* 39: */1,
316        /* 40: */Bytecode.IFZERO,
317        /* 41: */44,
318        /* 42: */Bytecode.CONST,
319        /* 43: */42,
320        /* 44: */Bytecode.RETURN};
321        assertPartialEvalEqualsAndRunsCorrect(new Program("manyIfsProgram", bytecodes, 0, 3));
322    }
323
324    public abstract static class Inst {
325        public abstract boolean execute(VirtualFrame frame);
326
327        public abstract int getTrueSucc();
328
329        public abstract int getFalseSucc();
330
331        public static class Const extends Inst {
332            private final FrameSlot slot;
333            private final int value;
334            private final int next;
335
336            public Const(FrameSlot slot, int value, int next) {
337                this.slot = slot;
338                this.value = value;
339                this.next = next;
340            }
341
342            @Override
343            public boolean execute(VirtualFrame frame) {
344                frame.setInt(slot, value);
345                return true;
346            }
347
348            @Override
349            public int getTrueSucc() {
350                return next;
351            }
352
353            @Override
354            public int getFalseSucc() {
355                return next;
356            }
357        }
358
359        public static class Return extends Inst {
360            public Return() {
361            }
362
363            @Override
364            public boolean execute(VirtualFrame frame) {
365                return true;
366            }
367
368            @Override
369            public int getTrueSucc() {
370                return -1;
371            }
372
373            @Override
374            public int getFalseSucc() {
375                return -1;
376            }
377        }
378
379        public static class IfZero extends Inst {
380            private final FrameSlot slot;
381            private final int thenInst;
382            private final int elseInst;
383
384            public IfZero(FrameSlot slot, int thenInst, int elseInst) {
385                this.slot = slot;
386                this.thenInst = thenInst;
387                this.elseInst = elseInst;
388            }
389
390            @Override
391            public boolean execute(VirtualFrame frame) {
392                return (FrameUtil.getIntSafe(frame, slot) == 0);
393            }
394
395            @Override
396            public int getTrueSucc() {
397                return thenInst;
398            }
399
400            @Override
401            public int getFalseSucc() {
402                return elseInst;
403            }
404        }
405
406        public static class IfLt extends Inst {
407            private final FrameSlot slot1;
408            private final FrameSlot slot2;
409            private final int thenInst;
410            private final int elseInst;
411
412            public IfLt(FrameSlot slot1, FrameSlot slot2, int thenInst, int elseInst) {
413                this.slot1 = slot1;
414                this.slot2 = slot2;
415                this.thenInst = thenInst;
416                this.elseInst = elseInst;
417            }
418
419            @Override
420            public boolean execute(VirtualFrame frame) {
421                return (FrameUtil.getIntSafe(frame, slot1) < FrameUtil.getIntSafe(frame, slot2));
422            }
423
424            @Override
425            public int getTrueSucc() {
426                return thenInst;
427            }
428
429            @Override
430            public int getFalseSucc() {
431                return elseInst;
432            }
433        }
434    }
435
436    public static class InstArrayProgram extends RootNode {
437        private final String name;
438        @CompilationFinal protected final Inst[] inst;
439        protected final FrameSlot returnSlot;
440
441        public InstArrayProgram(String name, Inst[] inst, FrameSlot returnSlot, FrameDescriptor fd) {
442            super(MockLanguage.class, null, fd);
443            this.name = name;
444            this.inst = inst;
445            this.returnSlot = returnSlot;
446        }
447
448        @Override
449        public String toString() {
450            return name;
451        }
452
453        @Override
454        @ExplodeLoop(merge = true)
455        public Object execute(VirtualFrame frame) {
456            int ip = 0;
457            while (ip != -1) {
458                CompilerAsserts.partialEvaluationConstant(ip);
459                if (inst[ip].execute(frame)) {
460                    ip = inst[ip].getTrueSucc();
461                } else {
462                    ip = inst[ip].getFalseSucc();
463                }
464            }
465            return FrameUtil.getIntSafe(frame, returnSlot);
466        }
467    }
468
469    @Test
470    public void instArraySimpleIfProgram() {
471        FrameDescriptor fd = new FrameDescriptor();
472        FrameSlot valueSlot = fd.addFrameSlot("value", FrameSlotKind.Int);
473        FrameSlot returnSlot = fd.addFrameSlot("return", FrameSlotKind.Int);
474        Inst[] inst = new Inst[]{
475        /* 0: */new Inst.Const(valueSlot, 1, 1),
476        /* 1: */new Inst.IfZero(valueSlot, 2, 4),
477        /* 2: */new Inst.Const(returnSlot, 41, 3),
478        /* 3: */new Inst.Return(),
479        /* 4: */new Inst.Const(returnSlot, 42, 5),
480        /* 5: */new Inst.Return()};
481        assertPartialEvalEqualsAndRunsCorrect(new InstArrayProgram("instArraySimpleIfProgram", inst, returnSlot, fd));
482    }
483
484    /**
485     * Slightly modified version to expose a partial evaluation bug with ExplodeLoop(merge=true).
486     */
487    public static class InstArrayProgram2 extends InstArrayProgram {
488        public InstArrayProgram2(String name, Inst[] inst, FrameSlot returnSlot, FrameDescriptor fd) {
489            super(name, inst, returnSlot, fd);
490        }
491
492        @Override
493        @ExplodeLoop(merge = true)
494        public Object execute(VirtualFrame frame) {
495            int ip = 0;
496            while (ip != -1) {
497                CompilerAsserts.partialEvaluationConstant(ip);
498                if (inst[ip].execute(frame)) {
499                    ip = inst[ip].getTrueSucc();
500                } else {
501                    ip = inst[ip].getFalseSucc();
502                }
503            }
504            if (frame.getArguments().length > 0) {
505                return new Random();
506            } else {
507                return FrameUtil.getIntSafe(frame, returnSlot);
508            }
509        }
510    }
511
512    @Ignore("produces a bad graph")
513    @Test
514    public void instArraySimpleIfProgram2() {
515        FrameDescriptor fd = new FrameDescriptor();
516        FrameSlot value1Slot = fd.addFrameSlot("value1", FrameSlotKind.Int);
517        FrameSlot value2Slot = fd.addFrameSlot("value2", FrameSlotKind.Int);
518        FrameSlot returnSlot = fd.addFrameSlot("return", FrameSlotKind.Int);
519        Inst[] inst = new Inst[]{
520        /* 0: */new Inst.Const(value1Slot, 100, 1),
521        /* 1: */new Inst.Const(value2Slot, 100, 2),
522        /* 2: */new Inst.IfLt(value1Slot, value2Slot, 3, 5),
523        /* 3: */new Inst.Const(returnSlot, 41, 4),
524        /* 4: */new Inst.Return(),
525        /* 5: */new Inst.Const(returnSlot, 42, 6),
526        /* 6: */new Inst.Return()};
527        InstArrayProgram program = new InstArrayProgram2("instArraySimpleIfProgram2", inst, returnSlot, fd);
528        program.execute(Truffle.getRuntime().createVirtualFrame(new Object[0], fd));
529        program.execute(Truffle.getRuntime().createVirtualFrame(new Object[1], fd));
530        assertPartialEvalEqualsAndRunsCorrect(program);
531    }
532}