├── .gitignore ├── examples ├── 00-basic-usage │ ├── README.md │ ├── libgccjit.c │ ├── example.ir │ ├── php.php │ ├── libjit.bc │ ├── llvm.bc │ ├── llvm.s │ ├── libgccjit.s │ ├── libjit.s │ ├── example.output │ └── example.php ├── 01-branching │ ├── README.md │ ├── libgccjit.s │ ├── example.ir │ ├── php.php │ ├── llvm.s │ ├── libgccjit.c │ ├── libjit.bc │ ├── llvm.bc │ ├── example.output │ ├── libjit.s │ └── example.php ├── 02-function_calls │ ├── README.md │ ├── libgccjit.bc │ ├── libgccjit.c │ ├── php.php │ ├── example.ir │ ├── libgccjit.s │ ├── llvm.bc │ ├── libjit.bc │ ├── llvm.s │ ├── example.output │ ├── example.php │ └── libjit.s ├── 05-imported_functions │ ├── libgccjit.c │ ├── php.php │ ├── example.ir │ ├── llvm.bc │ ├── libgccjit.s │ ├── llvm.s │ ├── example.output │ └── example.php ├── 04-structs │ ├── libgccjit.c │ ├── example.ir │ ├── libgccjit.s │ ├── llvm.s │ ├── php.php │ ├── libjit.bc │ ├── llvm.bc │ ├── example.output │ ├── libjit.s │ └── example.php ├── 03-iterated-function-calls │ ├── libgccjit.s │ ├── libgccjit.bc │ ├── libgccjit.c │ ├── example.output │ ├── php.php │ ├── example.ir │ ├── example.php │ ├── llvm.bc │ ├── llvm.s │ └── libjit.bc ├── README.md ├── rebuild.php └── common.php ├── rebuild.php ├── lib ├── IR │ ├── TerminalOp.php │ ├── Function_.php │ ├── Op │ │ ├── BinaryOp │ │ │ ├── Add.php │ │ │ ├── Div.php │ │ │ ├── EQ.php │ │ │ ├── GE.php │ │ │ ├── GT.php │ │ │ ├── LE.php │ │ │ ├── LT.php │ │ │ ├── Mod.php │ │ │ ├── Mul.php │ │ │ ├── NE.php │ │ │ ├── SL.php │ │ │ ├── SR.php │ │ │ ├── Sub.php │ │ │ ├── BitwiseAnd.php │ │ │ ├── BitwiseOr.php │ │ │ ├── BitwiseXor.php │ │ │ ├── LogicalAnd.php │ │ │ └── LogicalOr.php │ │ ├── UnaryOp │ │ │ ├── Cast.php │ │ │ ├── Minus.php │ │ │ ├── BitwiseNot.php │ │ │ └── LogicalNot.php │ │ ├── Free.php │ │ ├── Malloc.php │ │ ├── UnaryOp.php │ │ ├── Realloc.php │ │ ├── CallNoReturn.php │ │ ├── BinaryOp.php │ │ ├── ReturnVoid.php │ │ ├── FieldRead.php │ │ ├── FieldWrite.php │ │ ├── Call.php │ │ ├── ReturnValue.php │ │ ├── BlockCall.php │ │ └── ConditionalBlockCall.php │ ├── Function_ │ │ ├── Exported.php │ │ ├── Static_.php │ │ ├── AlwaysInline.php │ │ ├── Imported.php │ │ └── Implemented.php │ ├── Op.php │ ├── Value │ │ ├── Null.php │ │ ├── Constant.php │ │ ├── Value.php │ │ └── Local.php │ ├── OpAbstract.php │ ├── Value.php │ ├── Parameter.php │ └── Block.php ├── CompiledUnit.php ├── Backend.php ├── Type.php ├── Type │ ├── Volatile.php │ ├── Struct │ │ └── Field.php │ ├── Const_.php │ ├── Pointer.php │ ├── ArrayType.php │ ├── Struct.php │ └── Primitive.php ├── Context.php ├── Builder.php ├── Backend │ ├── PHP │ │ └── CompiledUnit.php │ ├── LIBGCCJIT │ │ └── CompiledUnit.php │ ├── LLVM │ │ └── CompiledUnit.php │ ├── LIBJIT │ │ └── CompiledUnit.php │ ├── PHP.php │ └── LIBGCCJIT.php ├── TypeAbstract.php ├── BackendAbstract.php ├── Builder │ ├── TypeBuilder.php │ ├── GlobalBuilder.php │ ├── ConstBuilder.php │ ├── FunctionBuilder.php │ └── BlockBuilder.php └── Printer.php ├── phpunit.xml.dist ├── composer.json ├── CONTRIBUTING.md ├── ffi └── rebuild.php ├── README.md ├── CODE_OF_CONDUCT.md └── test └── Generic └── CompilerTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /examples/00-basic-usage/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/01-branching/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/02-function_calls/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/00-basic-usage/libgccjit.c: -------------------------------------------------------------------------------- 1 | extern long long 2 | add (long long a, long long b) 3 | { 4 | main: 5 | return a + b; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /rebuild.php: -------------------------------------------------------------------------------- 1 | , long long $1) { 3 | 4 | main(): 5 | #2 = Add($0, $1); 6 | ReturnValue #2; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /examples/05-imported_functions/libgccjit.c: -------------------------------------------------------------------------------- 1 | extern int 2 | abs (int n); /* (imported) */ 3 | 4 | extern int 5 | test (int a) 6 | { 7 | main: 8 | return abs (a); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/05-imported_functions/php.php: -------------------------------------------------------------------------------- 1 | ) { 4 | 5 | main(): 6 | #1 = Call(abs, $0); 7 | ReturnValue #1; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /lib/IR/TerminalOp.php: -------------------------------------------------------------------------------- 1 | , long long $1) { 7 | struct testA $2; 8 | 9 | main(): 10 | $2.a = $0; 11 | $2.b = $1; 12 | #3 = $2.a; 13 | #4 = $2.b; 14 | #5 = Add(#3, #4); 15 | ReturnValue #5; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /examples/04-structs/libgccjit.s: -------------------------------------------------------------------------------- 1 | .file "fake.c" 2 | .text 3 | .p2align 4,,15 4 | .globl add 5 | .type add, @function 6 | add: 7 | .LFB1: 8 | .cfi_startproc 9 | .L4: 10 | leaq (%rdi,%rsi), %rax 11 | ret 12 | .cfi_endproc 13 | .LFE1: 14 | .size add, .-add 15 | .ident "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406" 16 | .section .note.GNU-stack,"",@progbits 17 | -------------------------------------------------------------------------------- /examples/00-basic-usage/libgccjit.s: -------------------------------------------------------------------------------- 1 | .file "fake.c" 2 | .text 3 | .p2align 4,,15 4 | .globl add 5 | .type add, @function 6 | add: 7 | .LFB1: 8 | .cfi_startproc 9 | .L4: 10 | leaq (%rdi,%rsi), %rax 11 | ret 12 | .cfi_endproc 13 | .LFE1: 14 | .size add, .-add 15 | .ident "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406" 16 | .section .note.GNU-stack,"",@progbits 17 | -------------------------------------------------------------------------------- /examples/02-function_calls/example.ir: -------------------------------------------------------------------------------- 1 | 2 | long long add(long long $0, long long $1) { 3 | 4 | main(): 5 | #2 = Add($0, $1); 6 | ReturnValue #2; 7 | 8 | } 9 | 10 | long long add2(long long $0, long long $1) { 11 | 12 | main(): 13 | #2 = Call(add, $0, $1); 14 | #3 = Call(add, #2, $1); 15 | ReturnValue #3; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /examples/02-function_calls/libgccjit.s: -------------------------------------------------------------------------------- 1 | .file "fake.c" 2 | .text 3 | .p2align 4,,15 4 | .globl add2 5 | .type add2, @function 6 | add2: 7 | .LFB3: 8 | .cfi_startproc 9 | .L4: 10 | leaq (%rdi,%rsi,2), %rax 11 | ret 12 | .cfi_endproc 13 | .LFE3: 14 | .size add2, .-add2 15 | .ident "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406" 16 | .section .note.GNU-stack,"",@progbits 17 | -------------------------------------------------------------------------------- /examples/04-structs/llvm.s: -------------------------------------------------------------------------------- 1 | .text 2 | .file "test_0" 3 | .globl add 4 | .p2align 4, 0x90 5 | .type add,@function 6 | add: 7 | .cfi_startproc 8 | movq %rdi, -16(%rsp) 9 | movq %rsi, -8(%rsp) 10 | addq -16(%rsp), %rsi 11 | movq %rsi, %rax 12 | retq 13 | .Lfunc_end0: 14 | .size add, .Lfunc_end0-add 15 | .cfi_endproc 16 | 17 | 18 | .section ".note.GNU-stack","",@progbits 19 | -------------------------------------------------------------------------------- /examples/04-structs/php.php: -------------------------------------------------------------------------------- 1 | a = $p_0; 10 | $l_0->b = $p_1; 11 | $t_3 = $l_0->a; 12 | $t_4 = $l_0->b; 13 | $t_5 = $t_3 + $t_4; 14 | return $t_5; 15 | } 16 | -------------------------------------------------------------------------------- /lib/Backend.php: -------------------------------------------------------------------------------- 1 | , long long $1) { 3 | 4 | main(): 5 | #2 = GT($0, (long long) 0); 6 | IF (#2): 7 | jump ifTrue($1); 8 | ELSE: 9 | jump ifFalse($1); 10 | 11 | ifTrue(#3): 12 | #4 = Add(#3, (long long) 1); 13 | ReturnValue #4; 14 | 15 | ifFalse(#5): 16 | #6 = Add(#5, (long long) 2); 17 | ReturnValue #6; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /lib/IR/Value.php: -------------------------------------------------------------------------------- 1 | type = $type; 13 | } 14 | 15 | public function isOwnedBy(Block $block): bool { 16 | return true; 17 | } 18 | 19 | public function isConstant(): bool { 20 | return false; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /lib/Type.php: -------------------------------------------------------------------------------- 1 | parent = $parent; 14 | } 15 | 16 | public function asCString(): string { 17 | return 'volatile ' . $this->parent->asCString(); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /examples/01-branching/php.php: -------------------------------------------------------------------------------- 1 | 0; 5 | if ($t_5) { 6 | $bs_0 = $p_1; 7 | goto ifTrue; 8 | 9 | } else { 10 | $bs_1 = $p_1; 11 | goto ifFalse; 12 | 13 | } 14 | ifTrue: 15 | $ba_6 = $bs_0; 16 | $t_7 = $ba_6 + 1; 17 | return $t_7; 18 | ifFalse: 19 | $ba_8 = $bs_1; 20 | $t_9 = $ba_8 + 2; 21 | return $t_9; 22 | } 23 | -------------------------------------------------------------------------------- /examples/01-branching/llvm.s: -------------------------------------------------------------------------------- 1 | .text 2 | .file "test_0" 3 | .globl add1or2 4 | .p2align 4, 0x90 5 | .type add1or2,@function 6 | add1or2: 7 | .cfi_startproc 8 | testq %rdi, %rdi 9 | jle .LBB0_2 10 | movq %rsi, -8(%rsp) 11 | movq -8(%rsp), %rax 12 | incq %rax 13 | retq 14 | .LBB0_2: 15 | movq %rsi, -16(%rsp) 16 | movq -16(%rsp), %rax 17 | addq $2, %rax 18 | retq 19 | .Lfunc_end0: 20 | .size add1or2, .Lfunc_end0-add1or2 21 | .cfi_endproc 22 | 23 | 24 | .section ".note.GNU-stack","",@progbits 25 | -------------------------------------------------------------------------------- /lib/IR/Value/Constant.php: -------------------------------------------------------------------------------- 1 | value = $value; 15 | } 16 | 17 | public function isConstant(): bool { 18 | return true; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /examples/01-branching/libgccjit.c: -------------------------------------------------------------------------------- 1 | extern long long 2 | add1or2 (long long shouldAdd1, long long a) 3 | { 4 | long long ifTrue_arg_0; 5 | long long ifFalse_arg_0; 6 | 7 | main: 8 | if (shouldAdd1 > (long long)0) goto main_if; else goto main_else; 9 | 10 | ifTrue: 11 | return ifTrue_arg_0 + (long long)1; 12 | 13 | ifFalse: 14 | return ifFalse_arg_0 + (long long)2; 15 | 16 | main_if: 17 | ifTrue_arg_0 = a; 18 | goto ifTrue; 19 | 20 | main_else: 21 | ifFalse_arg_0 = a; 22 | goto ifFalse; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/01-branching/libjit.bc: -------------------------------------------------------------------------------- 1 | function add1or2(l1 : long, l2 : long) : long 2 | incoming_reg(l1, rdi) 3 | incoming_reg(l2, rsi) 4 | .L: 5 | .L0: 6 | i10 = l1 > 0 7 | if l1 > 0 then goto .L3 8 | .L: 9 | l9 = l2 10 | goto .L2 11 | ends_in_dead 12 | .L: 13 | .L3: 14 | l8 = l2 15 | goto .L1 16 | ends_in_dead 17 | .L: 18 | .L1: 19 | l11 = l8 20 | l12 = l11 + 1 21 | return_long(l12) 22 | ends_in_dead 23 | .L: 24 | .L2: 25 | l13 = l9 26 | l14 = l13 + 2 27 | return_long(l14) 28 | ends_in_dead 29 | .L: 30 | .L: 31 | end 32 | 33 | -------------------------------------------------------------------------------- /lib/Context.php: -------------------------------------------------------------------------------- 1 | owner = $owner; 17 | $this->name = $name; 18 | $this->type = $type; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /lib/Type/Const_.php: -------------------------------------------------------------------------------- 1 | parent = $parent; 16 | } 17 | 18 | public function asCString(): string { 19 | return 'const ' . $this->parent->asCString(); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /lib/Type/Pointer.php: -------------------------------------------------------------------------------- 1 | parent = $parent; 16 | } 17 | 18 | public function asCString(): string { 19 | return $this->parent->asCString() . '*'; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /lib/IR/Op/Free.php: -------------------------------------------------------------------------------- 1 | value = $value; 17 | } 18 | 19 | public function getArguments(): array { 20 | return [$this->value]; 21 | } 22 | 23 | public function getResult(): ?Value { 24 | return null; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /lib/IR/Op/Malloc.php: -------------------------------------------------------------------------------- 1 | return = $return; 17 | } 18 | 19 | public function getArguments(): array { 20 | return []; 21 | } 22 | 23 | public function getResult(): ?Value { 24 | return $this->return; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /examples/02-function_calls/libjit.bc: -------------------------------------------------------------------------------- 1 | function add(l1 : long, l2 : long) : long 2 | incoming_reg(l1, rdi) 3 | incoming_reg(l2, rsi) 4 | .L: 5 | .L0: 6 | l5 = l1 + l2 7 | return_long(l5) 8 | ends_in_dead 9 | .L: 10 | .L: 11 | end 12 | 13 | function add2(l1 : long, l2 : long) : long 14 | incoming_reg(l1, rdi) 15 | incoming_reg(l2, rsi) 16 | .L: 17 | .L0: 18 | outgoing_reg(l2, rsi) 19 | outgoing_reg(l1, rdi) 20 | call add 21 | .L: 22 | return_reg(l7, rax) 23 | outgoing_reg(l2, rsi) 24 | outgoing_reg(l7, rdi) 25 | call add 26 | .L: 27 | return_reg(l11, rax) 28 | return_long(l11) 29 | ends_in_dead 30 | .L: 31 | .L: 32 | end 33 | 34 | -------------------------------------------------------------------------------- /lib/IR/Parameter.php: -------------------------------------------------------------------------------- 1 | type = $type; 15 | $this->name = $name; 16 | } 17 | 18 | public function setValue(Value $value): void { 19 | if (isset($this->value)) { 20 | throw new \LogicException("Attempt to re-use parameter"); 21 | } 22 | $this->value = $value; 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /lib/IR/Op/UnaryOp.php: -------------------------------------------------------------------------------- 1 | value = $value; 16 | $this->result = $result; 17 | } 18 | 19 | public function getArguments(): array { 20 | return [$this->value]; 21 | } 22 | 23 | public function getResult(): ?Value { 24 | return $this->result; 25 | } 26 | } -------------------------------------------------------------------------------- /lib/Builder.php: -------------------------------------------------------------------------------- 1 | context = $context; 12 | $this->parent = $parent; 13 | } 14 | 15 | public function __call($name, array $args) { 16 | if ($this->parent !== null) { 17 | return $this->parent->$name($args); 18 | } 19 | throw new \LogicException('Call to unknown method ' . $name); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /lib/IR/Value/Value.php: -------------------------------------------------------------------------------- 1 | block = $block; 16 | } 17 | 18 | public function isOwnedBy(Block $block): bool { 19 | return $this->block === $block; 20 | } 21 | 22 | public function isConstant(): bool { 23 | return false; 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /lib/IR/Op/Realloc.php: -------------------------------------------------------------------------------- 1 | value = $value; 18 | $this->return = $return; 19 | } 20 | 21 | public function getArguments(): array { 22 | return [$this->value]; 23 | } 24 | 25 | public function getResult(): ?Value { 26 | return $this->return; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /examples/04-structs/llvm.bc: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'test_0' 2 | source_filename = "test_0" 3 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 4 | 5 | %testA = type { i64, i64 } 6 | 7 | define i64 @add(i64, i64) { 8 | main: 9 | %c = alloca %testA 10 | %a = getelementptr inbounds %testA, %testA* %c, i32 0, i32 0 11 | store i64 %0, i64* %a 12 | %b = getelementptr inbounds %testA, %testA* %c, i32 0, i32 1 13 | store i64 %1, i64* %b 14 | %a1 = getelementptr inbounds %testA, %testA* %c, i32 0, i32 0 15 | %a2 = load i64, i64* %a1 16 | %b3 = getelementptr inbounds %testA, %testA* %c, i32 0, i32 1 17 | %b4 = load i64, i64* %b3 18 | %var_0 = add i64 %a2, %b4 19 | ret i64 %var_0 20 | } 21 | -------------------------------------------------------------------------------- /lib/Type/ArrayType.php: -------------------------------------------------------------------------------- 1 | parent = $parent; 17 | $this->numElements = $numElements; 18 | } 19 | 20 | public function asCString(): string { 21 | return $this->parent->asCString() . '[' . $this->numElements . ']'; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /lib/IR/Op/CallNoReturn.php: -------------------------------------------------------------------------------- 1 | function = $function; 17 | $this->parameters = $parameters; 18 | } 19 | 20 | public function getArguments(): array { 21 | return $this->parameters; 22 | } 23 | 24 | public function getResult(): ?Value { 25 | return null; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /lib/IR/Op/BinaryOp.php: -------------------------------------------------------------------------------- 1 | left = $left; 17 | $this->right = $right; 18 | $this->result = $result; 19 | } 20 | 21 | public function getArguments(): array { 22 | return [$this->left, $this->right]; 23 | } 24 | 25 | public function getResult(): ?Value { 26 | return $this->result; 27 | } 28 | } -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/libgccjit.s: -------------------------------------------------------------------------------- 1 | .file "fake.c" 2 | .text 3 | .p2align 4,,15 4 | .globl add100 5 | .type add100, @function 6 | add100: 7 | .LFB3: 8 | .cfi_startproc 9 | .L4: 10 | leaq (%rdi,%rsi,8), %rax 11 | leaq (%rax,%rsi,8), %rax 12 | leaq (%rax,%rsi,8), %rax 13 | leaq (%rax,%rsi,8), %rax 14 | leaq (%rax,%rsi,8), %rax 15 | leaq (%rax,%rsi,8), %rax 16 | leaq (%rax,%rsi,8), %rax 17 | leaq (%rax,%rsi,8), %rax 18 | leaq (%rax,%rsi,8), %rax 19 | leaq (%rax,%rsi,8), %rax 20 | leaq (%rax,%rsi,8), %rax 21 | leaq (%rax,%rsi,8), %rax 22 | leaq (%rax,%rsi,4), %rax 23 | ret 24 | .cfi_endproc 25 | .LFE3: 26 | .size add100, .-add100 27 | .ident "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406" 28 | .section .note.GNU-stack,"",@progbits 29 | -------------------------------------------------------------------------------- /lib/IR/Op/ReturnVoid.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | ./test 15 | 16 | 17 | 18 | 19 | 20 | ./lib/ 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/IR/Value/Local.php: -------------------------------------------------------------------------------- 1 | name = $name; 18 | $this->function = $function; 19 | } 20 | 21 | public function isOwnedBy(Block $block): bool { 22 | return true; 23 | } 24 | 25 | public function isConstant(): bool { 26 | return false; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /lib/IR/Op/FieldRead.php: -------------------------------------------------------------------------------- 1 | struct = $struct; 19 | $this->field = $field; 20 | $this->return = $return; 21 | } 22 | 23 | public function getArguments(): array { 24 | return [$this->struct]; 25 | } 26 | 27 | public function getResult(): ?Value { 28 | return $this->return; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /lib/IR/Op/FieldWrite.php: -------------------------------------------------------------------------------- 1 | struct = $struct; 19 | $this->field = $field; 20 | $this->value = $value; 21 | } 22 | 23 | public function getArguments(): array { 24 | return [$this->struct, $this->value]; 25 | } 26 | 27 | public function getResult(): ?Value { 28 | return null; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /lib/IR/Op/Call.php: -------------------------------------------------------------------------------- 1 | function = $function; 18 | $this->return = $return; 19 | $this->parameters = $parameters; 20 | } 21 | 22 | public function getArguments(): array { 23 | return $this->parameters; 24 | } 25 | 26 | public function getResult(): ?Value { 27 | return $this->return; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /examples/05-imported_functions/example.output: -------------------------------------------------------------------------------- 1 | Test Function: 2 | int test(int a) { 3 | return abs(a); 4 | } 5 | Time to Compile: 6 | libgccjit: 0.030630 seconds 7 | llvm: 0.000716 seconds 8 | php: 0.000188 seconds 9 | 10 | 11 | Testing test: 12 | Compiler libgccjit 13 | test(1) = 1 14 | test(-1) = 1 15 | test(10) = 10 16 | test(-10) = 10 17 | Compiler llvm 18 | test(1) = 1 19 | test(-1) = 1 20 | test(10) = 10 21 | test(-10) = 10 22 | Compiler php 23 | test(1) = 1 24 | test(-1) = 1 25 | test(10) = 10 26 | test(-10) = 10 27 | 28 | Benchmarking 29 | Done 30 | 31 | Benchmark Results: 32 | libgccjit: 0.14208 seconds 33 | llvm: 0.14868 seconds 34 | php: 0.09192 seconds 35 | php closure: 0.04973 seconds 36 | 37 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ircmaxell/php-compiler-toolkit", 3 | "description": "An abstraction around several compiler libraries", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Anthony Ferrara", 8 | "email": "ircmaxell@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "ext-ffi": "*", 13 | "php": ">=7.4" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^8.0", 17 | "ircmaxell/ffime": "dev-master" 18 | }, 19 | "minimum-stability": "dev", 20 | "autoload": { 21 | "psr-4": { 22 | "PHPCompilerToolkit\\": "lib/" 23 | }, 24 | "files": [ 25 | "ffi/libgccjit.php", 26 | "ffi/libjit.php", 27 | "ffi/llvm.php" 28 | ] 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/00-basic-usage/libjit.s: -------------------------------------------------------------------------------- 1 | function add(long, long) : long 2 | 3 | /tmp/libjit-dump.o: file format elf64-x86-64 4 | 5 | 6 | Disassembly of section .text: 7 | 8 | 00007f66cbf20148 <.text>: 9 | 7f66cbf20148: 55 push %rbp 10 | 7f66cbf20149: 48 8b ec mov %rsp,%rbp 11 | 7f66cbf2014c: 48 83 ec 10 sub $0x10,%rsp 12 | 7f66cbf20150: 48 89 7d f8 mov %rdi,-0x8(%rbp) 13 | 7f66cbf20154: 48 89 75 f0 mov %rsi,-0x10(%rbp) 14 | 7f66cbf20158: 48 8b 45 f8 mov -0x8(%rbp),%rax 15 | 7f66cbf2015c: 48 03 45 f0 add -0x10(%rbp),%rax 16 | 7f66cbf20160: 48 8b e5 mov %rbp,%rsp 17 | 7f66cbf20163: 5d pop %rbp 18 | 7f66cbf20164: c3 retq 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /lib/IR/Op/ReturnValue.php: -------------------------------------------------------------------------------- 1 | value = $value; 16 | } 17 | 18 | public function getArguments(): array { 19 | return [$this->value]; 20 | } 21 | 22 | public function getResult(): ?Value { 23 | return null; 24 | } 25 | 26 | public function getTargetBlocks(): array { 27 | return []; 28 | } 29 | 30 | public function getBlockCallForBlock(Block $block): BlockCall { 31 | throw new \LogicException("Returns do not have target blocks"); 32 | } 33 | } -------------------------------------------------------------------------------- /examples/02-function_calls/llvm.s: -------------------------------------------------------------------------------- 1 | .text 2 | .file "test_0" 3 | .p2align 4, 0x90 4 | .type add,@function 5 | add: 6 | .cfi_startproc 7 | leaq (%rdi,%rsi), %rax 8 | retq 9 | .Lfunc_end0: 10 | .size add, .Lfunc_end0-add 11 | .cfi_endproc 12 | 13 | .globl add2 14 | .p2align 4, 0x90 15 | .type add2,@function 16 | add2: 17 | .cfi_startproc 18 | pushq %r14 19 | .Lcfi0: 20 | .cfi_def_cfa_offset 16 21 | pushq %rbx 22 | .Lcfi1: 23 | .cfi_def_cfa_offset 24 24 | pushq %rax 25 | .Lcfi2: 26 | .cfi_def_cfa_offset 32 27 | .Lcfi3: 28 | .cfi_offset %rbx, -24 29 | .Lcfi4: 30 | .cfi_offset %r14, -16 31 | movq %rsi, %rbx 32 | movabsq $add, %r14 33 | callq *%r14 34 | movq %rax, %rdi 35 | movq %rbx, %rsi 36 | callq *%r14 37 | addq $8, %rsp 38 | popq %rbx 39 | popq %r14 40 | retq 41 | .Lfunc_end1: 42 | .size add2, .Lfunc_end1-add2 43 | .cfi_endproc 44 | 45 | 46 | .section ".note.GNU-stack","",@progbits 47 | -------------------------------------------------------------------------------- /examples/01-branching/llvm.bc: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'test_0' 2 | source_filename = "test_0" 3 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 4 | 5 | define i64 @add1or2(i64, i64) { 6 | main: 7 | %ifTrue_arg_0 = alloca i64 8 | %ifFalse_arg_0 = alloca i64 9 | %var_0 = icmp sgt i64 %0, 0 10 | br i1 %var_0, label %main_if, label %main_else 11 | 12 | ifTrue: ; preds = %main_if 13 | %ifTrue_arg_0_load = load i64, i64* %ifTrue_arg_0 14 | %var_1 = add i64 %ifTrue_arg_0_load, 1 15 | ret i64 %var_1 16 | 17 | ifFalse: ; preds = %main_else 18 | %ifFalse_arg_0_load = load i64, i64* %ifFalse_arg_0 19 | %var_2 = add i64 %ifFalse_arg_0_load, 2 20 | ret i64 %var_2 21 | 22 | main_if: ; preds = %main 23 | store i64 %1, i64* %ifTrue_arg_0 24 | br label %ifTrue 25 | 26 | main_else: ; preds = %main 27 | store i64 %1, i64* %ifFalse_arg_0 28 | br label %ifFalse 29 | } 30 | -------------------------------------------------------------------------------- /examples/00-basic-usage/example.output: -------------------------------------------------------------------------------- 1 | Add Function: 2 | long long add(long long a, long long b) { 3 | return a + b; 4 | } 5 | Time to Compile: 6 | libjit: 0.000502 seconds 7 | libgccjit: 0.025765 seconds 8 | llvm: 0.000583 seconds 9 | php: 0.000177 seconds 10 | 11 | 12 | Testing add: 13 | Compiler libjit 14 | add(1, 1) = 2 15 | add(1, 2) = 3 16 | add(99, 1) = 100 17 | add(add(1, 2), 3) = 6 18 | Compiler libgccjit 19 | add(1, 1) = 2 20 | add(1, 2) = 3 21 | add(99, 1) = 100 22 | add(add(1, 2), 3) = 6 23 | Compiler llvm 24 | add(1, 1) = 2 25 | add(1, 2) = 3 26 | add(99, 1) = 100 27 | add(add(1, 2), 3) = 6 28 | Compiler php 29 | add(1, 1) = 2 30 | add(1, 2) = 3 31 | add(99, 1) = 100 32 | add(add(1, 2), 3) = 6 33 | 34 | Benchmarking 35 | Done 36 | 37 | Benchmark Results: 38 | libjit: 0.13496 seconds 39 | libgccjit: 0.14458 seconds 40 | llvm: 0.16913 seconds 41 | php: 0.06893 seconds 42 | php closure: 0.04753 seconds 43 | 44 | -------------------------------------------------------------------------------- /examples/04-structs/example.output: -------------------------------------------------------------------------------- 1 | Add Function: 2 | long long add(long long a, long long b) { 3 | return a + b; 4 | } 5 | Time to Compile: 6 | libjit: 0.000952 seconds 7 | libgccjit: 0.038853 seconds 8 | llvm: 0.000832 seconds 9 | php: 0.000283 seconds 10 | 11 | 12 | Testing add: 13 | Compiler libjit 14 | add(1, 1) = 2 15 | add(1, 2) = 3 16 | add(99, 1) = 100 17 | add(add(1, 2), 3) = 6 18 | Compiler libgccjit 19 | add(1, 1) = 2 20 | add(1, 2) = 3 21 | add(99, 1) = 100 22 | add(add(1, 2), 3) = 6 23 | Compiler llvm 24 | add(1, 1) = 2 25 | add(1, 2) = 3 26 | add(99, 1) = 100 27 | add(add(1, 2), 3) = 6 28 | Compiler php 29 | add(1, 1) = 2 30 | add(1, 2) = 3 31 | add(99, 1) = 100 32 | add(add(1, 2), 3) = 6 33 | 34 | Benchmarking 35 | Done 36 | 37 | Benchmark Results: 38 | libjit: 0.19674 seconds 39 | libgccjit: 0.16837 seconds 40 | llvm: 0.17368 seconds 41 | php: 0.20833 seconds 42 | php closure: 0.17697 seconds 43 | 44 | -------------------------------------------------------------------------------- /lib/IR/Op/BlockCall.php: -------------------------------------------------------------------------------- 1 | block = $block; 18 | } 19 | 20 | public function getArguments(): array { 21 | return $this->arguments; 22 | } 23 | 24 | public function getResult(): ?Value { 25 | return null; 26 | } 27 | 28 | public function getTargetBlocks(): array { 29 | return [$this->block]; 30 | } 31 | 32 | public function getBlockCallForBlock(Block $block): BlockCall { 33 | if ($block === $this->block) { 34 | return $this; 35 | } 36 | throw new \LogicException("Attempt to get BlockCall for non-existing block"); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /lib/IR/Function_/Imported.php: -------------------------------------------------------------------------------- 1 | context = $context; 23 | $this->name = $name; 24 | $this->returnType = $returnType; 25 | $this->parameters = $parameters; 26 | foreach ($parameters as $parameter) { 27 | $this->parametersByName[$parameter->name] = $parameter; 28 | } 29 | $this->isVariadic = $isVariadic; 30 | $context->imports[] = $this; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/libgccjit.bc: -------------------------------------------------------------------------------- 1 | static inline long long 2 | add (long long a, long long b) 3 | { 4 | main: 5 | return a + b; 6 | } 7 | 8 | extern long long 9 | add100 (long long a, long long b) 10 | { 11 | main: 12 | return add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (a, b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/libgccjit.c: -------------------------------------------------------------------------------- 1 | static inline long long 2 | add (long long a, long long b) 3 | { 4 | main: 5 | return a + b; 6 | } 7 | 8 | extern long long 9 | add100 (long long a, long long b) 10 | { 11 | main: 12 | return add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (add (a, b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b), b); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /examples/02-function_calls/example.output: -------------------------------------------------------------------------------- 1 | Add2 Function: 2 | long long add(long long a, long long b) { 3 | return a + b; 4 | } 5 | long long add2(long long a, long long b) { 6 | return add(add(a, b), b); 7 | } 8 | Time to Compile: 9 | libjit: 0.000527 seconds 10 | libgccjit: 0.025149 seconds 11 | llvm: 0.000596 seconds 12 | php: 0.000244 seconds 13 | 14 | 15 | Testing add2: 16 | Compiler libjit 17 | add2(1, 1) = 3 18 | add2(1, 2) = 5 19 | add2(99, 1) = 101 20 | add2(add2(1, 2), 3) = 11 21 | Compiler libgccjit 22 | add2(1, 1) = 3 23 | add2(1, 2) = 5 24 | add2(99, 1) = 101 25 | add2(add2(1, 2), 3) = 11 26 | Compiler llvm 27 | add2(1, 1) = 3 28 | add2(1, 2) = 5 29 | add2(99, 1) = 101 30 | add2(add2(1, 2), 3) = 11 31 | Compiler php 32 | add2(1, 1) = 3 33 | add2(1, 2) = 5 34 | add2(99, 1) = 101 35 | add2(add2(1, 2), 3) = 11 36 | 37 | Benchmarking 38 | Done 39 | 40 | Benchmark Results: 41 | libjit: 0.16521 seconds 42 | libgccjit: 0.18040 seconds 43 | llvm: 0.24001 seconds 44 | php: 0.21692 seconds 45 | php closure: 0.15103 seconds 46 | 47 | -------------------------------------------------------------------------------- /examples/01-branching/example.output: -------------------------------------------------------------------------------- 1 | Add1or2 Function: 2 | long long add1or2(long long shouldAdd1, long long a) { 3 | if (shouldAdd > 0) { 4 | return a + 1; 5 | } 6 | return a + 2; 7 | } 8 | Time to Compile: 9 | libjit: 0.000605 seconds 10 | libgccjit: 0.042911 seconds 11 | llvm: 0.000714 seconds 12 | php: 0.000221 seconds 13 | 14 | 15 | Testing add1or2: 16 | Compiler libjit 17 | add1or2(1, 1) = 2 18 | add1or2(1, 2) = 3 19 | add1or2(99, 1) = 2 20 | add1or2(add1or2(1, 2), 3) = 4 21 | Compiler libgccjit 22 | add1or2(1, 1) = 2 23 | add1or2(1, 2) = 3 24 | add1or2(99, 1) = 2 25 | add1or2(add1or2(1, 2), 3) = 4 26 | Compiler llvm 27 | add1or2(1, 1) = 2 28 | add1or2(1, 2) = 3 29 | add1or2(99, 1) = 2 30 | add1or2(add1or2(1, 2), 3) = 4 31 | Compiler php 32 | add1or2(1, 1) = 2 33 | add1or2(1, 2) = 3 34 | add1or2(99, 1) = 2 35 | add1or2(add1or2(1, 2), 3) = 4 36 | 37 | Benchmarking 38 | Done 39 | 40 | Benchmark Results: 41 | libjit: 0.14805 seconds 42 | libgccjit: 0.13896 seconds 43 | llvm: 0.14144 seconds 44 | php: 0.09502 seconds 45 | php closure: 0.04925 seconds 46 | 47 | -------------------------------------------------------------------------------- /lib/Backend/PHP/CompiledUnit.php: -------------------------------------------------------------------------------- 1 | backend = $backend; 17 | $this->code = $code; 18 | $this->declaredFunctions = $declaredFunctions; 19 | } 20 | 21 | public function getCallable(string $functionName): callable { 22 | if (!isset($this->declaredFunctions[$functionName])) { 23 | throw new \LogicException("Unable to get callable for unknown function $functionName"); 24 | } 25 | return $this->declaredFunctions[$functionName]; 26 | } 27 | 28 | 29 | public function dumpToFile(string $filename): void { 30 | file_put_contents($filename . '.php', 'code); 31 | } 32 | 33 | public function dumpCompiledToFile(string $filename): void { 34 | file_put_contents($filename . '.php', 'code); 35 | } 36 | } -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/example.output: -------------------------------------------------------------------------------- 1 | Add100 Function: 2 | long long add(long long a, long long b) { 3 | return a + b; 4 | } 5 | long long add100(long long a, long long b) { 6 | a = add(a, b) 7 | a = add(a, b) 8 | // 100 of these 9 | return a; 10 | } 11 | Time to Compile: 12 | libjit: 0.001262 seconds 13 | libgccjit: 0.030333 seconds 14 | llvm: 0.001038 seconds 15 | php: 0.000414 seconds 16 | 17 | 18 | Testing add100: 19 | Compiler libjit 20 | add100(1, 1) = 101 21 | add100(1, 2) = 201 22 | add100(99, 1) = 199 23 | add100(add100(1, 2), 3) = 501 24 | Compiler libgccjit 25 | add100(1, 1) = 101 26 | add100(1, 2) = 201 27 | add100(99, 1) = 199 28 | add100(add100(1, 2), 3) = 501 29 | Compiler llvm 30 | add100(1, 1) = 101 31 | add100(1, 2) = 201 32 | add100(99, 1) = 199 33 | add100(add100(1, 2), 3) = 501 34 | Compiler php 35 | add100(1, 1) = 101 36 | add100(1, 2) = 201 37 | add100(99, 1) = 199 38 | add100(add100(1, 2), 3) = 501 39 | 40 | Benchmarking 41 | Done 42 | 43 | Benchmark Results: 44 | libjit: 0.35324 seconds 45 | libgccjit: 0.14231 seconds 46 | llvm: 0.38155 seconds 47 | php: 4.49203 seconds 48 | php closure: 2.61538 seconds 49 | 50 | -------------------------------------------------------------------------------- /examples/04-structs/libjit.s: -------------------------------------------------------------------------------- 1 | function add(long, long) : long 2 | 3 | /tmp/libjit-dump.o: file format elf64-x86-64 4 | 5 | 6 | Disassembly of section .text: 7 | 8 | 00007fdf32619148 <.text>: 9 | 7fdf32619148: 55 push %rbp 10 | 7fdf32619149: 48 8b ec mov %rsp,%rbp 11 | 7fdf3261914c: 48 83 ec 20 sub $0x20,%rsp 12 | 7fdf32619150: 48 89 7d f8 mov %rdi,-0x8(%rbp) 13 | 7fdf32619154: 48 89 75 f0 mov %rsi,-0x10(%rbp) 14 | 7fdf32619158: 48 8d 45 e0 lea -0x20(%rbp),%rax 15 | 7fdf3261915c: 48 8b 4d f8 mov -0x8(%rbp),%rcx 16 | 7fdf32619160: 48 89 08 mov %rcx,(%rax) 17 | 7fdf32619163: 48 8d 45 e0 lea -0x20(%rbp),%rax 18 | 7fdf32619167: 48 8b 4d f0 mov -0x10(%rbp),%rcx 19 | 7fdf3261916b: 48 89 48 08 mov %rcx,0x8(%rax) 20 | 7fdf3261916f: 48 8d 45 e0 lea -0x20(%rbp),%rax 21 | 7fdf32619173: 48 8b 00 mov (%rax),%rax 22 | 7fdf32619176: 48 8d 4d e0 lea -0x20(%rbp),%rcx 23 | 7fdf3261917a: 48 8b 49 08 mov 0x8(%rcx),%rcx 24 | 7fdf3261917e: 48 03 c1 add %rcx,%rax 25 | 7fdf32619181: 48 8b e5 mov %rbp,%rsp 26 | 7fdf32619184: 5d pop %rbp 27 | 7fdf32619185: c3 retq 28 | 29 | end 30 | 31 | -------------------------------------------------------------------------------- /lib/IR/Op/ConditionalBlockCall.php: -------------------------------------------------------------------------------- 1 | cond = $cond; 20 | $this->ifTrue = new BlockCall($ifTrue); 21 | $this->ifFalse = new BlockCall($ifFalse); 22 | } 23 | 24 | public function getArguments(): array { 25 | return [$this->cond]; 26 | } 27 | 28 | public function getResult(): ?Value { 29 | return null; 30 | } 31 | 32 | public function getTargetBlocks(): array { 33 | return [$this->ifTrue->block, $this->ifFalse->block]; 34 | } 35 | 36 | public function getBlockCallForBlock(Block $block): BlockCall { 37 | if ($block === $this->ifTrue->block) { 38 | return $this->ifTrue; 39 | } 40 | if ($block === $this->ifFalse->block) { 41 | return $this->ifFalse; 42 | } 43 | 44 | throw new \LogicException("Attempt to get BlockCall for non-existing block"); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /examples/00-basic-usage/example.php: -------------------------------------------------------------------------------- 1 | type()->long_long() ; 19 | // Next, we need to create the function: name, returnType, isVariadic, Parameter ... 20 | $func = $builder->exportFunction('add', $type, false, new Parameter($type, 'a'), new Parameter($type, 'b')); 21 | // We need a block in the function (blocks contain code) 22 | $main = $func->createBlock('main'); 23 | 24 | $result = $main->add($func->arg(0), $func->arg(1)); 25 | // We want the block to return the result of addition of the two args: 26 | $main->returnValue($result); 27 | 28 | $builder->finish(); 29 | 30 | 31 | echo "Add Function: 32 | long long add(long long a, long long b) { 33 | return a + b; 34 | } 35 | "; 36 | 37 | generateResults( 38 | $context, 39 | // current directory 40 | __DIR__, 41 | // fn name 42 | 'add', 43 | // tests 44 | [[1, 1], [1, 2], [99, 1], [[1, 2], 3]], 45 | // benchmark iterations 46 | 1000000, 47 | // baseline implementation 48 | function(int $a, int $b): int { 49 | return $a + $b; 50 | } 51 | ); 52 | -------------------------------------------------------------------------------- /examples/01-branching/libjit.s: -------------------------------------------------------------------------------- 1 | function add1or2(long, long) : long 2 | 3 | /tmp/libjit-dump.o: file format elf64-x86-64 4 | 5 | 6 | Disassembly of section .text: 7 | 8 | 00007ff1ddb0d13f <.text>: 9 | 7ff1ddb0d13f: 55 push %rbp 10 | 7ff1ddb0d140: 48 8b ec mov %rsp,%rbp 11 | 7ff1ddb0d143: 48 83 ec 20 sub $0x20,%rsp 12 | 7ff1ddb0d147: 4c 89 34 24 mov %r14,(%rsp) 13 | 7ff1ddb0d14b: 4c 89 7c 24 08 mov %r15,0x8(%rsp) 14 | 7ff1ddb0d150: 4c 8b ff mov %rdi,%r15 15 | 7ff1ddb0d153: 4c 8b f6 mov %rsi,%r14 16 | 7ff1ddb0d156: 49 83 ff 00 cmp $0x0,%r15 17 | 7ff1ddb0d15a: 0f 8f 05 00 00 00 jg 0x7ff1ddb0d165 18 | 7ff1ddb0d160: e9 0f 00 00 00 jmpq 0x7ff1ddb0d174 19 | 7ff1ddb0d165: 49 8b c6 mov %r14,%rax 20 | 7ff1ddb0d168: 48 89 45 f8 mov %rax,-0x8(%rbp) 21 | 7ff1ddb0d16c: 48 ff c0 inc %rax 22 | 7ff1ddb0d16f: e9 0b 00 00 00 jmpq 0x7ff1ddb0d17f 23 | 7ff1ddb0d174: 49 8b c6 mov %r14,%rax 24 | 7ff1ddb0d177: 48 89 45 f0 mov %rax,-0x10(%rbp) 25 | 7ff1ddb0d17b: 48 83 c0 02 add $0x2,%rax 26 | 7ff1ddb0d17f: 4c 8b 34 24 mov (%rsp),%r14 27 | 7ff1ddb0d183: 4c 8b 7c 24 08 mov 0x8(%rsp),%r15 28 | 7ff1ddb0d188: 48 8b e5 mov %rbp,%rsp 29 | 7ff1ddb0d18b: 5d pop %rbp 30 | 7ff1ddb0d18c: c3 retq 31 | 32 | end 33 | 34 | -------------------------------------------------------------------------------- /lib/IR/Block.php: -------------------------------------------------------------------------------- 1 | function = $function; 16 | $this->name = $name; 17 | } 18 | 19 | public function addOp(Op $op) { 20 | if ($this->isClosed) { 21 | throw new \LogicException("Attempting to add op to closed block"); 22 | } 23 | foreach ($op->getArguments() as $arg) { 24 | if (!$arg->isOwnedBy($this)) { 25 | throw new \LogicException("Arg doesn't belong to block, this is not correct"); 26 | } 27 | } 28 | $result = $op->getResult(); 29 | if ($result !== null && !$result->isOwnedBy($this)) { 30 | throw new \LogicException("Result doesn't belong to block, this is not correct"); 31 | } 32 | if ($op instanceof TerminalOp) { 33 | $this->isClosed = true; 34 | } 35 | $this->ops[] = $op; 36 | } 37 | 38 | public function getBlockCallForBlock(Block $block): Op\BlockCall { 39 | foreach ($this->ops as $op) { 40 | if ($op instanceof TerminalOp) { 41 | return $op->getBlockCallForBlock($block); 42 | } 43 | } 44 | throw new \LogicException("Exactly one TerminalOp expected in each block"); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /lib/IR/Function_/Implemented.php: -------------------------------------------------------------------------------- 1 | context = $context; 26 | $this->name = $name; 27 | $this->returnType = $returnType; 28 | $this->parameters = $parameters; 29 | foreach ($parameters as $parameter) { 30 | $this->parametersByName[$parameter->name] = $parameter; 31 | } 32 | $this->isVariadic = $isVariadic; 33 | $context->functions[] = $this; 34 | } 35 | 36 | public function createBlock(string $name): Block { 37 | $block = new Block($this, $name); 38 | if (empty($this->blocks)) { 39 | foreach ($this->parameters as $parameter) { 40 | $parameter->setValue(new Value\Value($block, $parameter->type)); 41 | } 42 | } 43 | $this->blocks[] = $block; 44 | return $block; 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. 4 | 5 | When contributing to this repository, please first discuss that new features be discussed via issue before making a change. 6 | 7 | Please report any bugs to the issues page, due to the fact this project is done in free time, it may take a while to process the issues, but security vulnerabilities will have precedence over feature requests. 8 | 9 | Please note we have a code of conduct, [CODE\_OF\_CONDUCT.md](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 10 | 11 | # Pull Request Process 12 | 13 | All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult 14 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. 15 | 16 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. 17 | 2. Update the [README.md](README.md) with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters (if applicable). 18 | 3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 19 | 4. You may merge the Pull Request in once you have the sign-off of other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. -------------------------------------------------------------------------------- /examples/05-imported_functions/example.php: -------------------------------------------------------------------------------- 1 | type()->long_long(); 15 | 16 | $abs = $builder->importFunction('abs', $builder->type()->int(), false, new Parameter($builder->type()->int(), 'n')); 17 | 18 | 19 | // Next, we need to create the function: name, returnType, isVariadic, Parameter ... 20 | $func = $builder->exportFunction('test', $builder->type()->int(), false, new Parameter($builder->type()->int(), 'a')); 21 | 22 | // We need a block in the function (blocks contain code) 23 | $main = $func->createBlock('main'); 24 | 25 | $result = $main->call($abs, $func->arg(0)); 26 | // We want the block to return the result of addition of the two args: 27 | $main->returnValue($result); 28 | 29 | $builder->finish(); 30 | 31 | 32 | echo "Test Function: 33 | int test(int a) { 34 | return abs(a); 35 | } 36 | "; 37 | 38 | $compilerSet = getCompilerSet(); 39 | // libjit doesn't support imported functions for now 40 | $compilerSet->libjit = false; 41 | 42 | generateResults( 43 | $context, 44 | // current directory 45 | __DIR__, 46 | // fn name 47 | 'test', 48 | // tests 49 | [[1], [-1], [10], [-10]], 50 | // benchmark iterations 51 | 1000000, 52 | // baseline implementation 53 | function(int $a): int { 54 | return abs($a); 55 | }, 56 | $compilerSet 57 | ); 58 | -------------------------------------------------------------------------------- /lib/TypeAbstract.php: -------------------------------------------------------------------------------- 1 | context = $context; 16 | $this->context->types[] = $this; 17 | } 18 | 19 | public function getContext(): Context { 20 | return $this->context; 21 | } 22 | 23 | public function getPointer(): Type { 24 | if ($this->pointer === null) { 25 | $this->pointer = new Type\Pointer($this->context, $this); 26 | } 27 | return $this->pointer; 28 | } 29 | 30 | public function getConst(): Type { 31 | if ($this->const === null) { 32 | $this->const = new Type\Const_($this->context, $this); 33 | } 34 | return $this->const; 35 | } 36 | 37 | public function getVolatile(): Type { 38 | if ($this->volatile === null) { 39 | $this->volatile = new Type\Volatile($this->context, $this); 40 | } 41 | return $this->volatile; 42 | } 43 | 44 | public function newArrayType(int $numElements): Type { 45 | if (!isset($this->array[$numElements])) { 46 | $this->array[$numElements] = new Type\ArrayType($this->context, $this, $numElements); 47 | } 48 | return $this->array[$numElements]; 49 | } 50 | 51 | public function isSigned(): bool { 52 | return false; 53 | } 54 | 55 | public function isFloatingPoint(): bool { 56 | return false; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /examples/04-structs/example.php: -------------------------------------------------------------------------------- 1 | type()->long_long(); 18 | 19 | $struct = $builder->type()->struct("testA"); 20 | $struct->createField('a', $long)->createField('b', $long); 21 | 22 | // long long add(long long a, long long b) { 23 | // struct {long long a; long long b} c; 24 | // c.a = a; 25 | // c.b = b; 26 | // return c.a + c.b; 27 | // } 28 | 29 | $add = $builder->exportFunction('add', $long, false, new Parameter($long, 'a'), new Parameter($long, 'b')); 30 | $local = $add->createLocal('c', $struct); 31 | 32 | $main = $add->createBlock('main'); 33 | 34 | $main->writeField($local, 'a', $add->arg(0)); 35 | $main->writeField($local, 'b', $add->arg(1)); 36 | 37 | $result = $main->add($main->readField($local, 'a'), $main->readField($local, 'b')); 38 | 39 | $main->returnValue($result); 40 | 41 | $builder->finish(); 42 | 43 | echo "Add Function: 44 | long long add(long long a, long long b) { 45 | return a + b; 46 | } 47 | "; 48 | 49 | generateResults( 50 | $context, 51 | // current directory 52 | __DIR__, 53 | // fn name 54 | 'add', 55 | // tests 56 | [[1, 1], [1, 2], [99, 1], [[1, 2], 3]], 57 | // benchmark iterations 58 | 1000000, 59 | // baseline implementation 60 | function(int $a, int $b): int { 61 | $struct = new \StdClass; 62 | $struct->a = $a; 63 | $struct->b = $b; 64 | return $struct->a + $struct->b; 65 | } 66 | ); 67 | 68 | 69 | -------------------------------------------------------------------------------- /lib/Type/Struct.php: -------------------------------------------------------------------------------- 1 | name = $name; 18 | $this->fields = $fields; 19 | } 20 | 21 | public function createField(string $name, Type $type): self { 22 | $this->addField(new Struct\Field($this, $name, $type)); 23 | return $this; 24 | } 25 | 26 | public function addField(Struct\Field ... $fields): self { 27 | foreach ($fields as $field) { 28 | if ($field->owner !== $this) { 29 | throw new \LogicException("Attempting to add a field from a different struct"); 30 | } 31 | } 32 | $this->fields = array_merge($this->fields, $fields); 33 | return $this; 34 | } 35 | 36 | public function fieldOffset(Struct\Field $field): int { 37 | foreach ($this->fields as $index => $tmp) { 38 | if ($tmp === $field) { 39 | return $index; 40 | } 41 | } 42 | throw new \LogicException("Attempt to fetch offset for unknown field: {$field->name}"); 43 | } 44 | 45 | public function field(string $name): Struct\Field { 46 | foreach ($this->fields as $field) { 47 | if ($field->name === $name) { 48 | return $field; 49 | } 50 | } 51 | throw new \LogicException("Attempt to fetch unknown field $name"); 52 | } 53 | 54 | public function asCString(): string { 55 | return 'struct ' . $this->name; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /lib/Backend/LIBGCCJIT/CompiledUnit.php: -------------------------------------------------------------------------------- 1 | backend = $backend; 22 | $this->context = $context; 23 | $this->result = $result; 24 | $this->signatures = $signatures; 25 | } 26 | 27 | public function getCallable(string $functionName): callable { 28 | if (!isset($this->signatures[$functionName])) { 29 | throw new \LogicException("Unable to get callable for unknown function $functionName"); 30 | } 31 | $code = $this->backend->lib->gcc_jit_result_get_code($this->result, $functionName); 32 | $cb = $this->backend->lib->getFFI()->new($this->signatures[$functionName]); 33 | FFI::memcpy( 34 | FFI::addr($cb), 35 | FFI::addr($code->getData()), 36 | FFI::sizeof($cb) 37 | ); 38 | return $cb; 39 | } 40 | 41 | public function dumpToFile(string $filename): void { 42 | $this->backend->lib->gcc_jit_context_dump_to_file($this->context, $filename . '.c', 1); 43 | } 44 | 45 | public function dumpCompiledToFile(string $filename): void { 46 | $this->backend->lib->gcc_jit_context_compile_to_file($this->context, lib::GCC_JIT_OUTPUT_KIND_ASSEMBLER, $filename . '.s'); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /examples/02-function_calls/example.php: -------------------------------------------------------------------------------- 1 | type()->long_long(); 16 | 17 | // First, let's create an add(a, b) { return a + b; } function to call later: 18 | 19 | $add = $builder->staticFunction('add', $type, false, new Parameter($type, 'a'), new Parameter($type, 'b')); 20 | $main = $add->createBlock('main'); 21 | $result = $main->add($add->arg(0), $add->arg(1)); 22 | $main->returnValue($result); 23 | 24 | 25 | // Now, let's create a second function which calls add() 10 times on itself 26 | // long long add2(long long a, long long b) 27 | // a = add(a, b) 28 | // b = add(a, b) 29 | // return b; 30 | // } 31 | 32 | $add2 = $builder->exportFunction('add2', $type, false, new Parameter($type, 'a'), new Parameter($type, 'b')); 33 | $main = $add2->createBlock('main'); 34 | $r1 = $main->call($add, $add2->arg(0), $add2->arg(1)); 35 | $r2 = $main->call($add, $r1, $add2->arg(1)); 36 | $main->returnValue($r2); 37 | 38 | $builder->finish(); 39 | 40 | echo "Add2 Function: 41 | long long add(long long a, long long b) { 42 | return a + b; 43 | } 44 | long long add2(long long a, long long b) { 45 | return add(add(a, b), b); 46 | } 47 | "; 48 | 49 | function add(int $a, int $b): int { 50 | return $a + $b; 51 | } 52 | 53 | generateResults( 54 | $context, 55 | // current directory 56 | __DIR__, 57 | // fn name 58 | 'add2', 59 | // tests 60 | [[1, 1], [1, 2], [99, 1], [[1, 2], 3]], 61 | // benchmark iterations 62 | 1000000, 63 | // baseline implementation 64 | function(int $a, int $b): int { 65 | return add(add($a, $b), $b); 66 | } 67 | ); -------------------------------------------------------------------------------- /ffi/rebuild.php: -------------------------------------------------------------------------------- 1 | include('/opt/include/jit/jit.h'); 9 | $libjit->include('/opt/include/jit/jit-dump.h'); 10 | $libjit->codegen('libjit\\libjit', __DIR__ . '/libjit.php'); 11 | 12 | echo "Rebuilding LIBGCCJIT libraries\n"; 13 | 14 | $libgccjit = new FFIMe\FFIMe('/usr/lib/x86_64-linux-gnu/libgccjit.so.0'); 15 | $libgccjit->include('libgccjit.h'); 16 | $libgccjit->codegen('libgccjit\\libgccjit', __DIR__ . '/libgccjit.php'); 17 | 18 | echo "Rebuilding LLVM libraries\n"; 19 | 20 | $llvm = new FFIMe\FFIMe('/usr/lib/llvm-4.0/lib/libLLVM-4.0.so.1', ['/usr/include/llvm-c-4.0/', '/usr/include/llvm-4.0/']); 21 | $llvm->include("llvm-c/Core.h"); 22 | $llvm->include("llvm-c/TargetMachine.h"); 23 | $llvm->include("llvm-c/ExecutionEngine.h"); 24 | $llvm->include("llvm-c/Analysis.h"); 25 | $llvm->codegen('llvm\\llvm', __DIR__ . '/llvm.php'); 26 | 27 | $code = ' 28 | class string_ptr implements illvm { 29 | private FFI\CData $data; 30 | public function __construct(FFI\CData $data) { $this->data = $data; } 31 | public function getData(): FFI\CData { return $this->data; } 32 | public function equals(string_ptr $other): bool { return $this->data == $other->data; } 33 | public function addr(): string_ptr_ptr { return new string_ptr_ptr(FFI::addr($this->data)); } 34 | public static function getType(): string { return \'char**\'; } 35 | } 36 | class uint64_t implements illvm { 37 | private FFI\CData $data; 38 | public function __construct($data) { $tmp = FFI::new(\'unsigned long long\'); $tmp = $data; $this->data = $tmp; } 39 | public function getData(): FFI\CData { return $this->data; } 40 | public function equals(uint64_t $other): bool { return $this->data == $other->data; } 41 | public function addr(): uint64_t_ptr { return new uint64_t_ptr(FFI::addr($this->data)); } 42 | public static function getType(): string { return \'uint64_t\'; } 43 | } 44 | '; 45 | 46 | $orig = file_get_contents(__DIR__ . '/llvm.php'); 47 | file_put_contents(__DIR__ . '/llvm.php', $orig . $code); -------------------------------------------------------------------------------- /examples/02-function_calls/libjit.s: -------------------------------------------------------------------------------- 1 | function add(long, long) : long 2 | 3 | /tmp/libjit-dump.o: file format elf64-x86-64 4 | 5 | 6 | Disassembly of section .text: 7 | 8 | 00007f2b087ce258 <.text>: 9 | 7f2b087ce258: 55 push %rbp 10 | 7f2b087ce259: 48 8b ec mov %rsp,%rbp 11 | 7f2b087ce25c: 48 83 ec 10 sub $0x10,%rsp 12 | 7f2b087ce260: 48 89 7d f8 mov %rdi,-0x8(%rbp) 13 | 7f2b087ce264: 48 89 75 f0 mov %rsi,-0x10(%rbp) 14 | 7f2b087ce268: 48 8b 45 f8 mov -0x8(%rbp),%rax 15 | 7f2b087ce26c: 48 03 45 f0 add -0x10(%rbp),%rax 16 | 7f2b087ce270: 48 8b e5 mov %rbp,%rsp 17 | 7f2b087ce273: 5d pop %rbp 18 | 7f2b087ce274: c3 retq 19 | 20 | end 21 | 22 | function add2(long, long) : long 23 | 24 | /tmp/libjit-dump.o: file format elf64-x86-64 25 | 26 | 27 | Disassembly of section .text: 28 | 29 | 00007f2b087ce2a9 <.text>: 30 | 7f2b087ce2a9: 55 push %rbp 31 | 7f2b087ce2aa: 48 8b ec mov %rsp,%rbp 32 | 7f2b087ce2ad: 48 83 ec 20 sub $0x20,%rsp 33 | 7f2b087ce2b1: 4c 89 3c 24 mov %r15,(%rsp) 34 | 7f2b087ce2b5: 48 89 7d f8 mov %rdi,-0x8(%rbp) 35 | 7f2b087ce2b9: 4c 8b fe mov %rsi,%r15 36 | 7f2b087ce2bc: 49 8b f7 mov %r15,%rsi 37 | 7f2b087ce2bf: 48 8b 7d f8 mov -0x8(%rbp),%rdi 38 | 7f2b087ce2c3: b8 08 00 00 00 mov $0x8,%eax 39 | 7f2b087ce2c8: e8 8b ff ff ff callq 0x7f2b087ce258 40 | 7f2b087ce2cd: 49 8b f7 mov %r15,%rsi 41 | 7f2b087ce2d0: 48 8b f8 mov %rax,%rdi 42 | 7f2b087ce2d3: 48 89 45 f0 mov %rax,-0x10(%rbp) 43 | 7f2b087ce2d7: b8 08 00 00 00 mov $0x8,%eax 44 | 7f2b087ce2dc: e8 77 ff ff ff callq 0x7f2b087ce258 45 | 7f2b087ce2e1: 4c 8b 3c 24 mov (%rsp),%r15 46 | 7f2b087ce2e5: 48 8b e5 mov %rbp,%rsp 47 | 7f2b087ce2e8: 5d pop %rbp 48 | 7f2b087ce2e9: c3 retq 49 | 50 | end 51 | 52 | -------------------------------------------------------------------------------- /examples/01-branching/example.php: -------------------------------------------------------------------------------- 1 | type()->long_long(); 17 | 18 | 19 | // long long add1or2(long long shouldAdd1, long long a) { 20 | // if (shouldAdd > 0) { 21 | // return a + 1; 22 | // } 23 | // return a + 2; 24 | // } 25 | 26 | // We need to create the function: name, returnType, isVariadic, Parameter ... 27 | $func = $builder->exportFunction('add1or2', $type, false, new Parameter($type, 'shouldAdd1'), new Parameter($type, 'a')); 28 | // We need a block in the function (blocks contain code) 29 | $main = $func->createBlock('main'); 30 | // Now create our two blocks for after the conditional 31 | $ifTrue = $func->createBlock('ifTrue'); 32 | $ifFalse = $func->createBlock('ifFalse'); 33 | // shouldAdd > 0 34 | $cond = $main->gt($func->arg(0), $builder->const()->long_long(0)); 35 | // if ($cond) $ifTrue else $ifFalse; 36 | $main->jumpIf($cond, $ifTrue, $ifFalse); 37 | // return a + 1 38 | $ifTrue->returnValue($ifTrue->add($func->arg(1), $builder->const()->long_long(1))); 39 | // return a + 2 40 | $ifFalse->returnValue($ifFalse->add($func->arg(1), $builder->const()->long_long(2))); 41 | 42 | $builder->finish(); 43 | 44 | echo "Add1or2 Function: 45 | long long add1or2(long long shouldAdd1, long long a) { 46 | if (shouldAdd > 0) { 47 | return a + 1; 48 | } 49 | return a + 2; 50 | } 51 | "; 52 | 53 | generateResults( 54 | $context, 55 | // current directory 56 | __DIR__, 57 | // fn name 58 | 'add1or2', 59 | // tests 60 | [[1, 1], [1, 2], [99, 1], [[1, 2], 3]], 61 | // benchmark iterations 62 | 1000000, 63 | // baseline implementation 64 | function(int $shouldAdd, int $a): int { 65 | if ($shouldAdd > 0) { 66 | return $a + 1; 67 | } 68 | return $a + 2; 69 | } 70 | ); 71 | -------------------------------------------------------------------------------- /lib/Backend/LLVM/CompiledUnit.php: -------------------------------------------------------------------------------- 1 | backend = $backend; 25 | $this->module = $module; 26 | $this->engine = $engine; 27 | $this->signatures = $signatures; 28 | $this->optimizationlevel = $optimizationlevel; 29 | } 30 | 31 | public function getCallable(string $functionName): callable { 32 | if (!isset($this->signatures[$functionName])) { 33 | throw new \LogicException("Unable to get callable for unknown function $functionName"); 34 | } 35 | $code = $this->backend->lib->LLVMGetFunctionAddress($this->engine, $functionName); 36 | $cb = $this->backend->lib->getFFI()->new($this->signatures[$functionName]); 37 | FFI::memcpy( 38 | FFI::addr($cb), 39 | FFI::addr($code->getData()), 40 | FFI::sizeof($cb) 41 | ); 42 | return $cb; 43 | } 44 | 45 | 46 | public function dumpToFile(string $filename): void { 47 | $error = new string_ptr(FFI::addr(FFI::new('char*'))); 48 | $this->backend->lib->LLVMPrintModuleToFile($this->module, $filename . '.bc', $error); 49 | } 50 | 51 | public function dumpCompiledToFile(string $filename): void { 52 | $error = new string_ptr(FFI::addr(FFI::new('char*'))); 53 | $cgft = $this->backend->lib->getFFI()->new('LLVMCodeGenFileType'); 54 | $cgft = lib::LLVMAssemblyFile; 55 | $codegen = new LLVMCodeGenFileType($cgft); 56 | $machine = $this->backend->lib->LLVMGetExecutionEngineTargetMachine($this->engine); 57 | $this->backend->lib->LLVMTargetMachineEmitToFile($machine, $this->module, $filename . '.s', $codegen, $error); 58 | } 59 | } -------------------------------------------------------------------------------- /lib/Backend/LIBJIT/CompiledUnit.php: -------------------------------------------------------------------------------- 1 | backend = $backend; 20 | $this->functions = $functions; 21 | $this->signatures = $signatures; 22 | } 23 | 24 | public function getCallable(string $functionName): callable { 25 | if (!isset($this->functions[$functionName])) { 26 | throw new \LogicException("Unable to get callable for unknown function $functionName"); 27 | } 28 | $this->compile($functionName); 29 | $code = $this->backend->lib->jit_function_to_closure($this->functions[$functionName]); 30 | $cb = $this->backend->lib->getFFI()->new($this->signatures[$functionName]); 31 | FFI::memcpy( 32 | FFI::addr($cb), 33 | FFI::addr($code->getData()), 34 | FFI::sizeof($cb) 35 | ); 36 | return $cb; 37 | } 38 | 39 | public function dumpCompiledToFile(string $filename): void { 40 | $file = $this->backend->lib->fopen($filename . '.s', 'w'); 41 | foreach ($this->functions as $name => $func) { 42 | $this->compile($name); 43 | $this->backend->lib->jit_dump_function($file, $func, $name); 44 | } 45 | $this->backend->lib->fclose($file); 46 | } 47 | 48 | public function dumpToFile(string $filename): void { 49 | $file = $this->backend->lib->fopen($filename . '.bc', 'w'); 50 | foreach ($this->functions as $name => $func) { 51 | if (isset($this->compiledFunctions[$name])) { 52 | throw new \LogicException("Function is already compiled, cannot dump"); 53 | } 54 | $this->backend->lib->jit_dump_function($file, $func, $name); 55 | } 56 | $this->backend->lib->fclose($file); 57 | } 58 | 59 | protected function compile(string $functionName) { 60 | if (!isset($this->compiledFunctions[$functionName])) { 61 | $this->compiledFunctions[$functionName] = true; 62 | $this->backend->lib->jit_function_compile($this->functions[$functionName]); 63 | } 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A compiler toolkit for PHP 2 | 3 | This attempts to be a pretty heavy abstraction on top of (initially) three libraries: 4 | 5 | * [libgccjit](https://gcc.gnu.org/onlinedocs/gcc-7.2.0/jit/index.html) 6 | * [ligjit](https://www.gnu.org/software/libjit/) 7 | * [llvm](https://llvm.org/docs/) 8 | 9 | It also includes a PHP backend for compiling to PHP itself (though this will be slower than native PHP and should only really be used for debug purposes). 10 | 11 | As each project differs quite a bit once you get beyond the initial level, I'm not sure how useful a high level abstraction will be. 12 | 13 | However, this project aims to be a pluggable backend for [PHP-Compiler](https://github.com/ircmaxell/php-compiler). The primary goal is to assess performance of each compiler and resulting code. 14 | 15 | As such, the performance of this library isn't incredibly important today. Over time, it may become critical. 16 | 17 | # How to use? 18 | 19 | So, here's a "basic" example for a function which adds two numbers: 20 | 21 | ```php 22 | use PHPCompilerToolkit\Context; 23 | use PHPCompilerToolkit\Builder\GlobalBuilder; 24 | use PHPCompilerToolkit\IR\Parameter; 25 | 26 | $context = new Context; 27 | $builder = new GlobalBuilder($context); 28 | 29 | // long long add(long long a, long long b) { 30 | // return a + b; 31 | // } 32 | 33 | // First, let's get a reference to the type we want to use: 34 | $type = $builder->type()->longLong() ; 35 | // Next, we need to create the function: name, returnType, isVariadic, Parameter ... 36 | $func = $builder->createFunction('add', $type, false, new Parameter($type, 'a'), new Parameter($type, 'b')); 37 | // We need a block in the function (blocks contain code) 38 | $main = $func->createBlock('main'); 39 | // We want the block to return the result of addition of the two args: 40 | $main->returnValue($main->add($func->arg(0), $func->arg(1))); 41 | // We are done building everything 42 | $builder->finish(); 43 | ``` 44 | 45 | Notice how we haven't picked a backend yet. Now we can: 46 | 47 | ```php 48 | 49 | $libjit = new PHPCompilerToolkit\Backend\LIBJIT; 50 | 51 | $add = $libjit->compile($context)->getCallable('add'); 52 | ``` 53 | 54 | Or if we wanted to use libgccjit: 55 | 56 | ```php 57 | $libgccjit = new PHPCompilerToolkit\Backend\LIBGCCJIT; 58 | 59 | $add = $libgccjit->compile($context)->getCallable('add'); 60 | ``` 61 | 62 | Or if we wanted to use LLVM: 63 | 64 | ```php 65 | $llvm = new PHPCompilerToolkit\Backend\LLVM; 66 | 67 | $add3 = $llvm->compile($context)->getCallable('add'); 68 | ``` 69 | 70 | And since they are just normal PHP closures at this point: 71 | 72 | ```php 73 | var_dump($add(1, 1), $add2(2, 2), $add3(4, 4); 74 | // int(2), int(4), int(8) 75 | ``` -------------------------------------------------------------------------------- /lib/BackendAbstract.php: -------------------------------------------------------------------------------- 1 | beforeCompile($context, $optimizationLevel); 19 | $this->typeMap = new SplObjectStorage; 20 | $this->constantMap = new SplObjectStorage; 21 | $this->functionMap = []; 22 | foreach ($context->types as $type) { 23 | $this->typeMap[$type] = $this->compileType($type); 24 | } 25 | foreach ($context->constants as $constant) { 26 | $this->constantMap[$constant] = $this->compileConstant($constant); 27 | } 28 | foreach ($context->imports as $import) { 29 | $this->functionMap[$import->name] = $this->importFunction($import); 30 | } 31 | foreach ($context->functions as $function) { 32 | $this->functionMap[$function->name] = $this->declareFunction($function); 33 | if ($function instanceof Function_\Exported) { 34 | $this->signatureMap[$function->name] = $this->generateSignature($function); 35 | } 36 | } 37 | foreach ($context->functions as $function) { 38 | $this->compileFunction($function, $this->functionMap[$function->name]); 39 | } 40 | $result = $this->buildResult(); 41 | unset($this->typeMap); 42 | unset($this->functionMap); 43 | $this->afterCompile($context); 44 | return $result; 45 | } 46 | 47 | abstract protected function compileType(Type $type); 48 | abstract protected function compileConstant(Constant $constant); 49 | abstract protected function importFunction(Function_ $function); 50 | abstract protected function declareFunction(Function_ $function); 51 | abstract protected function compileFunction(Function_ $function, $func): void; 52 | abstract protected function buildResult(): CompiledUnit; 53 | 54 | protected function beforeCompile(Context $context, int $optimizationLevel): void { 55 | } 56 | 57 | protected function afterCompile(Context $context): void { 58 | } 59 | 60 | protected function generateSignature(Function_ $function): string { 61 | $params = []; 62 | foreach ($function->parameters as $parameter) { 63 | $params[] = $parameter->type->asCString(); 64 | } 65 | if ($function->isVariadic) { 66 | $params[] = "..."; 67 | } 68 | return $function->returnType->asCString() . '(*)(' . implode(', ', $params) . ')'; 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /lib/Type/Primitive.php: -------------------------------------------------------------------------------- 1 | 'void', 32 | self::T_VOID_PTR => 'void*', 33 | self::T_BOOL => 'bool', 34 | self::T_CHAR => 'char', 35 | self::T_SIGNED_CHAR => 'signed char', 36 | self::T_UNSIGNED_CHAR => 'unsigned char', 37 | self::T_SHORT => 'short', 38 | self::T_UNSIGNED_SHORT => 'unsigned short', 39 | self::T_INT => 'int', 40 | self::T_UNSIGNED_INT => 'unsigned int', 41 | self::T_LONG => 'long', 42 | self::T_UNSIGNED_LONG => 'unsigned long', 43 | self::T_LONG_LONG => 'long long', 44 | self::T_UNSIGNED_LONG_LONG => 'unsigned long long', 45 | self::T_FLOAT => 'float', 46 | self::T_DOUBLE => 'double', 47 | self::T_LONG_DOUBLE => 'long double', 48 | self::T_SIZE_T => 'size_t', 49 | ]; 50 | 51 | public int $kind; 52 | 53 | public function __construct(Context $context, int $kind) { 54 | parent::__construct($context); 55 | $this->kind = $kind; 56 | } 57 | 58 | public function asCString(): string { 59 | if (isset(self::PRIMITIVE_TYPE_MAP_TO_C[$this->kind])) { 60 | return self::PRIMITIVE_TYPE_MAP_TO_C[$this->kind]; 61 | } 62 | throw new \LogicException("Unknown kind to c type"); 63 | } 64 | 65 | public function isVoid(): bool { 66 | return $this->kind === self::T_VOID; 67 | } 68 | 69 | public function isSigned(): bool { 70 | switch ($this->kind) { 71 | case self::T_VOID_PTR: 72 | case self::T_UNSIGNED_CHAR: 73 | case self::T_UNSIGNED_SHORT: 74 | case self::T_UNSIGNED_INT: 75 | case self::T_UNSIGNED_LONG: 76 | case self::T_UNSIGNED_LONG_LONG: 77 | return false; 78 | } 79 | return true; 80 | } 81 | 82 | public function isFloatingPoint(): bool { 83 | switch ($this->kind) { 84 | case self::T_FLOAT: 85 | case self::T_DOUBLE: 86 | case self::T_LONG_DOUBLE: 87 | return true; 88 | } 89 | return false; 90 | } 91 | } -------------------------------------------------------------------------------- /lib/Builder/TypeBuilder.php: -------------------------------------------------------------------------------- 1 | primitive(Primitive::T_VOID); 21 | } 22 | 23 | public function void_ptr(): Type { 24 | return $this->primitive(Primitive::T_VOID_PTR); 25 | } 26 | 27 | public function bool(): Type { 28 | return $this->primitive(Primitive::T_BOOL); 29 | } 30 | 31 | public function char(): Type { 32 | return $this->primitive(Primitive::T_CHAR); 33 | } 34 | 35 | public function signed_char(): Type { 36 | return $this->primitive(Primitive::T_SIGNED_CHAR); 37 | } 38 | 39 | public function unsigned_char(): Type { 40 | return $this->primitive(Primitive::T_UNSIGNED_CHAR); 41 | } 42 | 43 | public function short(): Type { 44 | return $this->primitive(Primitive::T_SHORT); 45 | } 46 | 47 | public function unsigned_short(): Type { 48 | return $this->primitive(Primitive::T_UNSIGNED_SHORT); 49 | } 50 | 51 | public function int(): Type { 52 | return $this->primitive(Primitive::T_INT); 53 | } 54 | 55 | public function unsigned_int(): Type { 56 | return $this->primitive(Primitive::T_UNSIGNED_INT); 57 | } 58 | 59 | public function long(): Type { 60 | return $this->primitive(Primitive::T_LONG); 61 | } 62 | 63 | public function unsigned_long(): Type { 64 | return $this->primitive(Primitive::T_UNSIGNED_LONG); 65 | } 66 | 67 | public function long_long(): Type { 68 | return $this->primitive(Primitive::T_LONG_LONG); 69 | } 70 | 71 | public function unsigned_long_long(): Type { 72 | return $this->primitive(Primitive::T_UNSIGNED_LONG_LONG); 73 | } 74 | 75 | public function float(): Type { 76 | return $this->primitive(Primitive::T_FLOAT); 77 | } 78 | 79 | public function double(): Type { 80 | return $this->primitive(Primitive::T_DOUBLE); 81 | } 82 | 83 | public function long_double(): Type { 84 | return $this->primitive(Primitive::T_LONG_DOUBLE); 85 | } 86 | 87 | public function size_t(): Type { 88 | return $this->primitive(Primitive::T_SIZE_T); 89 | } 90 | 91 | public function primitive(int $kind) { 92 | if (!isset($this->primitives[$kind])) { 93 | $this->primitives[$kind] = new Type\Primitive($this->context, $kind); 94 | } 95 | return $this->primitives[$kind]; 96 | } 97 | 98 | public function struct(string $name): Type { 99 | if (!isset($this->structs[$name])) { 100 | $this->structs[$name] = new Type\Struct($this->context, $name); 101 | } 102 | return $this->structs[$name]; 103 | } 104 | 105 | 106 | } -------------------------------------------------------------------------------- /lib/Builder/GlobalBuilder.php: -------------------------------------------------------------------------------- 1 | type = new TypeBuilder($this->context, $this); 18 | $this->const = new ConstBuilder($this->context, $this); 19 | } 20 | 21 | public function exportFunction(string $name, Type $returnType, bool $isVariadic, Parameter ...$parameters): FunctionBuilder { 22 | $function = new FunctionBuilder( 23 | $this->context, 24 | $this, 25 | new Function_\Exported( 26 | $this->context, 27 | $name, 28 | $returnType, 29 | $isVariadic, 30 | ...$parameters 31 | ) 32 | ); 33 | $this->functions[$name] = $function; 34 | return $function; 35 | } 36 | 37 | public function staticFunction(string $name, Type $returnType, bool $isVariadic, Parameter ...$parameters): FunctionBuilder { 38 | $function = new FunctionBuilder( 39 | $this->context, 40 | $this, 41 | new Function_\Static_( 42 | $this->context, 43 | $name, 44 | $returnType, 45 | $isVariadic, 46 | ...$parameters 47 | ) 48 | ); 49 | $this->functions[$name] = $function; 50 | return $function; 51 | } 52 | 53 | public function alwaysInlineFunction(string $name, Type $returnType, bool $isVariadic, Parameter ...$parameters): FunctionBuilder { 54 | $function = new FunctionBuilder( 55 | $this->context, 56 | $this, 57 | new Function_\AlwaysInline( 58 | $this->context, 59 | $name, 60 | $returnType, 61 | $isVariadic, 62 | ...$parameters 63 | ) 64 | ); 65 | $this->functions[$name] = $function; 66 | return $function; 67 | } 68 | 69 | public function importFunction(string $name, Type $returnType, bool $isVariadic, Parameter ...$parameters): FunctionBuilder { 70 | $function = new FunctionBuilder( 71 | $this->context, 72 | $this, 73 | new Function_\Imported( 74 | $this->context, 75 | $name, 76 | $returnType, 77 | $isVariadic, 78 | ...$parameters 79 | ) 80 | ); 81 | $this->functions[$name] = $function; 82 | return $function; 83 | } 84 | 85 | public function type(): TypeBuilder { 86 | return $this->type; 87 | } 88 | 89 | public function const(): ConstBuilder { 90 | return $this->const; 91 | } 92 | 93 | public function finish(): void { 94 | foreach ($this->functions as $function) { 95 | $function->finish(); 96 | } 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Each folder here contains an example using the compiler toolkit `example.php`, and associated generated files. 4 | 5 | # Compilation Times 6 | 7 | Each example keeps track of how long it takes the backend to compile the code to native. The following table is a collation of the results: 8 | 9 | 10 | 11 | | Example Name | libgccjit | libjit | llvm | php | 12 | |-------------------------------|-------------|-------------|-------------|-------------| 13 | | 00-basic-usage | 0.025765 | 0.000502 | 0.000583 | 0.000177 | 14 | | 01-branching | 0.042911 | 0.000605 | 0.000714 | 0.000221 | 15 | | 02-function_calls | 0.025149 | 0.000527 | 0.000596 | 0.000244 | 16 | | 03-iterated-function-calls | 0.030333 | 0.001262 | 0.001038 | 0.000414 | 17 | | 04-structs | 0.038853 | 0.000952 | 0.000832 | 0.000283 | 18 | | 05-imported_functions | 0.030630 | | 0.000716 | 0.000188 | 19 | 20 | 21 | 22 | # Benchmark Results 23 | 24 | Each example includes a benchmark comparing compiled code to a PHP closure (anonymous function). Here are the latest results: 25 | 26 | 27 | 28 | | Example Name | libgccjit | libjit | llvm | php | php closure | 29 | |-------------------------------|-------------|-------------|-------------|-------------|-------------| 30 | | 00-basic-usage | 0.14458 | 0.13496 | 0.16913 | 0.06893 | 0.04753 | 31 | | 01-branching | 0.13896 | 0.14805 | 0.14144 | 0.09502 | 0.04925 | 32 | | 02-function_calls | 0.18040 | 0.16521 | 0.24001 | 0.21692 | 0.15103 | 33 | | 03-iterated-function-calls | 0.14231 | 0.35324 | 0.38155 | 4.49203 | 2.61538 | 34 | | 04-structs | 0.16837 | 0.19674 | 0.17368 | 0.20833 | 0.17697 | 35 | | 05-imported_functions | 0.14208 | | 0.14868 | 0.09192 | 0.04973 | 36 | 37 | 38 | 39 | # Generated Result Files 40 | 41 | ## Intermediary Result: `example.ir` 42 | 43 | This is representation of the function(s) being compiled, which is custom to this toolkit. 44 | 45 | ## Output: `example.output` 46 | 47 | The raw text output of the execution of `example.php` (what you would get if you ran it yourself) 48 | 49 | ## libjit bytecode: `libjit.bc` 50 | 51 | The libjit generated bytecode. 52 | 53 | ## libjit assembly: `libjit.s` 54 | 55 | The disassembled machine code that libjit generated while compiling this function 56 | 57 | ## libgccjit bytecode: `libgccjit.c` 58 | 59 | The libgccjit generated bytecode. Note that this is very similar to C, but not quite... 60 | 61 | ## libgccjit assembly: `libgccjit.s` 62 | 63 | The optimized assembly that libgccjit generated while compiling this function 64 | 65 | ## LLVM bytecode: `llvm.bc` 66 | 67 | The LLVM generated bytecode (IR). 68 | 69 | ## LLVM assembly: `llvm.s` 70 | 71 | The optimized assembly that LLVM generated while compiling this function 72 | 73 | ## PHP Output: `php.php` 74 | 75 | The compiled code is output as PHP! Woot! -------------------------------------------------------------------------------- /lib/Builder/ConstBuilder.php: -------------------------------------------------------------------------------- 1 | primitiveInt($value ? 0 : 1, Primitive::T_BOOL); 21 | } 22 | 23 | public function char(int $value): Value { 24 | return $this->primitiveInt($value, Primitive::T_CHAR); 25 | } 26 | 27 | public function signed_char(int $value): Value { 28 | return $this->primitiveInt($value, Primitive::T_SIGNED_CHAR); 29 | } 30 | 31 | public function unsigned_char(int $value): Value { 32 | return $this->primitiveInt($value, Primitive::T_UNSIGNED_CHAR); 33 | } 34 | 35 | public function short(int $value): Value { 36 | return $this->primitiveInt($value, Primitive::T_SHORT); 37 | } 38 | 39 | public function unsigned_short(int $value): Value { 40 | return $this->primitiveInt($value, Primitive::T_UNSIGNED_SHORT); 41 | } 42 | 43 | public function int(int $value): Value { 44 | return $this->primitiveInt($value, Primitive::T_INT); 45 | } 46 | 47 | public function unsigned_int(int $value): Value { 48 | return $this->primitiveInt($value, Primitive::T_UNSIGNED_INT); 49 | } 50 | 51 | public function long(int $value): Value { 52 | return $this->primitiveInt($value, Primitive::T_LONG); 53 | } 54 | 55 | public function unsigned_long(int $value): Value { 56 | return $this->primitiveInt($value, Primitive::T_UNSIGNED_LONG); 57 | } 58 | 59 | public function long_long(int $value): Value { 60 | return $this->primitiveInt($value, Primitive::T_LONG_LONG); 61 | } 62 | 63 | public function unsigned_long_long(int $value): Value { 64 | return $this->primitiveInt($value, Primitive::T_UNSIGNED_LONG_LONG); 65 | } 66 | 67 | public function float(float $value): Value { 68 | return $this->primitiveFloat($value, Primitive::T_FLOAT); 69 | } 70 | 71 | public function double(float $value): Value { 72 | return $this->primitiveFloat($value, Primitive::T_DOUBLE); 73 | } 74 | 75 | public function long_double(float $value): Value { 76 | return $this->primitiveFloat($value, Primitive::T_LONG_DOUBLE); 77 | } 78 | 79 | public function size_t(int $value): Value { 80 | return $this->primitiveInt($value, Primitive::T_SIZE_T); 81 | } 82 | 83 | public function primitiveInt(int $value, int $kind): Value { 84 | $type = $this->type()->primitive($kind); 85 | $const = new Value\Constant($value, $type); 86 | $this->context->constants[] = $const; 87 | return $const; 88 | } 89 | 90 | public function primitiveFloat(float $value, int $kind): Value { 91 | $type = $this->type()->primitive($kind); 92 | $const = new Value\Constant($value, $type); 93 | $this->context->constants[] = $const; 94 | return $const; 95 | } 96 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 7 | 8 | ## Our Standards 9 | 10 | Examples of behavior that contributes to creating a positive environment 11 | include: 12 | 13 | * Using welcoming and inclusive language 14 | * Being respectful of differing viewpoints and experiences 15 | * Gracefully accepting constructive criticism 16 | * Focusing on what is best for the community 17 | * Showing empathy towards other community members 18 | 19 | Examples of unacceptable behavior by participants include: 20 | 21 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 22 | * Trolling, insulting/derogatory comments, and personal or political attacks 23 | * Public or private harassment 24 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 25 | * Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Our Responsibilities 28 | 29 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 30 | 31 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 36 | 37 | Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 38 | 39 | ## Enforcement 40 | 41 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at me@ircmaxell.com. All 42 | complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. 43 | 44 | Further details of specific enforcement policies may be posted separately. 45 | 46 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 47 | 48 | ## Attribution 49 | 50 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 51 | 52 | [homepage](https://www.contributor-covenant.org): https://www.contributor-covenant.org 53 | 54 | For answers to common questions about this code of conduct, see 55 | https://www.contributor-covenant.org/faq 56 | -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/php.php: -------------------------------------------------------------------------------- 1 | createPrimitiveType($type); 14 | $this->assertInstanceOf(Type::class, $type, "$backendName failed to provide a valid type for $typeName"); 15 | $ptr = $type->getPointer(); 16 | $this->assertInstanceOf(Type::class, $type, "$backendName failed to provide a valid type for $typeName*"); 17 | $const = $type->getConst(); 18 | $this->assertInstanceOf(Type::class, $type, "$backendName failed to provide a valid type for const $typeName"); 19 | $volatile = $type->getVolatile(); 20 | $this->assertInstanceOf(Type::class, $type, "$backendName failed to provide a valid type for volatile $typeName"); 21 | } 22 | 23 | /** 24 | * @dataProvider provideBackends 25 | */ 26 | public function testBasicFunctionDeclaration(string $backendName, Compiler $compiler) { 27 | $void = $compiler->createPrimitiveType(Type::T_VOID); 28 | $func = $compiler->createFunction('test', $void, false); 29 | $this->assertInstanceOf(Function_::class, $func); 30 | $this->assertEquals($void, $func->getReturnType()); 31 | $this->assertEquals('test', $func->getName()); 32 | $this->assertEquals([], $func->getParameters()); 33 | $this->assertEquals(false, $func->isVariadic()); 34 | } 35 | 36 | public static function provideBackendAndTypes(): \Generator { 37 | foreach (self::provideBackends() as $backend) { 38 | yield [$backend[0], $backend[1], 'void', Type::T_VOID]; 39 | yield [$backend[0], $backend[1], 'void*', Type::T_VOID_PTR]; 40 | yield [$backend[0], $backend[1], 'bool', Type::T_BOOL]; 41 | yield [$backend[0], $backend[1], 'char', Type::T_CHAR]; 42 | yield [$backend[0], $backend[1], 'signed char', Type::T_SIGNED_CHAR]; 43 | yield [$backend[0], $backend[1], 'unsigned char', Type::T_UNSIGNED_CHAR]; 44 | yield [$backend[0], $backend[1], 'short', Type::T_SHORT]; 45 | yield [$backend[0], $backend[1], 'unsigned short', Type::T_UNSIGNED_SHORT]; 46 | yield [$backend[0], $backend[1], 'int', Type::T_INT]; 47 | yield [$backend[0], $backend[1], 'unsigned int', Type::T_UNSIGNED_INT]; 48 | yield [$backend[0], $backend[1], 'long', Type::T_LONG]; 49 | yield [$backend[0], $backend[1], 'unsigned long', Type::T_UNSIGNED_LONG]; 50 | yield [$backend[0], $backend[1], 'long long', Type::T_LONG_LONG]; 51 | yield [$backend[0], $backend[1], 'unsigned long long', Type::T_UNSIGNED_LONG_LONG]; 52 | yield [$backend[0], $backend[1], 'float', Type::T_FLOAT]; 53 | yield [$backend[0], $backend[1], 'double', Type::T_DOUBLE]; 54 | yield [$backend[0], $backend[1], 'long double', Type::T_LONG_DOUBLE]; 55 | yield [$backend[0], $backend[1], 'size_t', Type::T_SIZE_T]; 56 | } 57 | } 58 | 59 | public static function provideBackends(): \Generator { 60 | $it = new \DirectoryIterator(__DIR__ . '/../../lib/Backend'); 61 | foreach ($it as $file) { 62 | if ($file->isFile()) { 63 | continue; 64 | } 65 | $name = $file->getBasename(); 66 | if ($name === '.' || $name === '..') { 67 | continue; 68 | } 69 | $class = __NAMESPACE__ . '\\Backend\\' . $name . '\\Compiler'; 70 | yield [$name, new $class]; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /examples/rebuild.php: -------------------------------------------------------------------------------- 1 | isFile()) { 8 | continue; 9 | } 10 | $example = $file->getPathname() . '/example.php'; 11 | if (file_exists($example)) { 12 | echo " - Building Example " . $file->getBasename() . "\n"; 13 | ob_start(); 14 | passthru(escapeshellcmd(PHP_BINARY) . ' ' . escapeshellarg($example)); 15 | file_put_contents($file->getPathname() . '/example.output', ob_get_clean()); 16 | } 17 | } 18 | 19 | $benchmarks = []; 20 | $compileTimes = []; 21 | $it = new DirectoryIterator(__DIR__); 22 | foreach ($it as $file) { 23 | if ($file->isFile()) { 24 | continue; 25 | } 26 | $example = $file->getPathname() . '/example.output'; 27 | if (file_exists($example)) { 28 | $exampleName = $file->getBasename(); 29 | $exampleOutput = file_get_contents($example); 30 | $benchmarks[$exampleName] = extractBenchmark($exampleOutput); 31 | $compileTimes[$exampleName] = extractCompileTime($exampleOutput); 32 | } 33 | } 34 | 35 | ksort($benchmarks); 36 | ksort($compileTimes); 37 | 38 | $benchmarkTable = buildTable($benchmarks); 39 | $compileTimeTable = buildTable($compileTimes); 40 | 41 | $readme = file_get_contents(__DIR__ . '/README.md'); 42 | 43 | $readme = preg_replace('(()(.*)())ims', "\$1\n\n" . $benchmarkTable . "\n\$3", $readme); 44 | 45 | $readme = preg_replace('(()(.*)())ims', "\$1\n\n" . $compileTimeTable . "\n\$3", $readme); 46 | 47 | file_put_contents(__DIR__ . '/README.md', $readme); 48 | 49 | echo $table; 50 | 51 | echo "Done\n"; 52 | 53 | 54 | function extractBenchmark(string $output): array { 55 | $regex = '(^Benchmark Results:\s*(\\n\\s+[a-zA-Z0-9 ]+:\s*[0-9.]+\s+seconds\s*?)+)im'; 56 | preg_match($regex, $output, $match); 57 | $regex2 = '(^\s+([a-zA-Z0-9 ]+):\s*([0-9.]+)\s+seconds)im'; 58 | preg_match_all($regex2, $match[0], $matches, PREG_SET_ORDER); 59 | $result = []; 60 | foreach ($matches as $match) { 61 | $result[$match[1]] = $match[2]; 62 | } 63 | return $result; 64 | } 65 | 66 | function extractCompileTime(string $output): array { 67 | $regex = '(^Time to Compile:\s*(\\n\\s+[a-zA-Z0-9 ]+:\s*[0-9.]+\s+seconds\s*?)+)im'; 68 | preg_match($regex, $output, $match); 69 | $regex2 = '(^\s+([a-zA-Z0-9 ]+):\s*([0-9.]+)\s+seconds)im'; 70 | preg_match_all($regex2, $match[0], $matches, PREG_SET_ORDER); 71 | $result = []; 72 | foreach ($matches as $match) { 73 | $result[$match[1]] = $match[2]; 74 | } 75 | return $result; 76 | } 77 | 78 | function buildTable(array $times): string { 79 | $allbackends = []; 80 | foreach ($times as $results) { 81 | $allbackends = array_merge($allbackends, array_keys($results)); 82 | } 83 | $allbackends = array_unique($allbackends); 84 | sort($allbackends); 85 | 86 | $table = '| Example Name |'; 87 | foreach ($allbackends as $name) { 88 | $table .= sprintf('%12s |', $name); 89 | } 90 | 91 | $table .= "\n|-------------------------------|"; 92 | 93 | foreach ($allbackends as $name) { 94 | $table .= str_repeat('-', 13) . '|'; 95 | } 96 | $table .= "\n"; 97 | 98 | foreach ($times as $name => $results) { 99 | $table .= sprintf('|%30s |', $name); 100 | foreach ($allbackends as $backend) { 101 | $table .= sprintf('%12s |', isset($results[$backend]) ? $results[$backend] : ''); 102 | } 103 | $table .= "\n"; 104 | } 105 | return $table; 106 | } 107 | -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/example.ir: -------------------------------------------------------------------------------- 1 | 2 | long long add(long long $0, long long $1) { 3 | 4 | main(): 5 | #2 = Add($0, $1); 6 | ReturnValue #2; 7 | 8 | } 9 | 10 | long long add100(long long $0, long long $1) { 11 | 12 | main(): 13 | #2 = Call(add, $0, $1); 14 | #3 = Call(add, #2, $1); 15 | #4 = Call(add, #3, $1); 16 | #5 = Call(add, #4, $1); 17 | #6 = Call(add, #5, $1); 18 | #7 = Call(add, #6, $1); 19 | #8 = Call(add, #7, $1); 20 | #9 = Call(add, #8, $1); 21 | #10 = Call(add, #9, $1); 22 | #11 = Call(add, #10, $1); 23 | #12 = Call(add, #11, $1); 24 | #13 = Call(add, #12, $1); 25 | #14 = Call(add, #13, $1); 26 | #15 = Call(add, #14, $1); 27 | #16 = Call(add, #15, $1); 28 | #17 = Call(add, #16, $1); 29 | #18 = Call(add, #17, $1); 30 | #19 = Call(add, #18, $1); 31 | #20 = Call(add, #19, $1); 32 | #21 = Call(add, #20, $1); 33 | #22 = Call(add, #21, $1); 34 | #23 = Call(add, #22, $1); 35 | #24 = Call(add, #23, $1); 36 | #25 = Call(add, #24, $1); 37 | #26 = Call(add, #25, $1); 38 | #27 = Call(add, #26, $1); 39 | #28 = Call(add, #27, $1); 40 | #29 = Call(add, #28, $1); 41 | #30 = Call(add, #29, $1); 42 | #31 = Call(add, #30, $1); 43 | #32 = Call(add, #31, $1); 44 | #33 = Call(add, #32, $1); 45 | #34 = Call(add, #33, $1); 46 | #35 = Call(add, #34, $1); 47 | #36 = Call(add, #35, $1); 48 | #37 = Call(add, #36, $1); 49 | #38 = Call(add, #37, $1); 50 | #39 = Call(add, #38, $1); 51 | #40 = Call(add, #39, $1); 52 | #41 = Call(add, #40, $1); 53 | #42 = Call(add, #41, $1); 54 | #43 = Call(add, #42, $1); 55 | #44 = Call(add, #43, $1); 56 | #45 = Call(add, #44, $1); 57 | #46 = Call(add, #45, $1); 58 | #47 = Call(add, #46, $1); 59 | #48 = Call(add, #47, $1); 60 | #49 = Call(add, #48, $1); 61 | #50 = Call(add, #49, $1); 62 | #51 = Call(add, #50, $1); 63 | #52 = Call(add, #51, $1); 64 | #53 = Call(add, #52, $1); 65 | #54 = Call(add, #53, $1); 66 | #55 = Call(add, #54, $1); 67 | #56 = Call(add, #55, $1); 68 | #57 = Call(add, #56, $1); 69 | #58 = Call(add, #57, $1); 70 | #59 = Call(add, #58, $1); 71 | #60 = Call(add, #59, $1); 72 | #61 = Call(add, #60, $1); 73 | #62 = Call(add, #61, $1); 74 | #63 = Call(add, #62, $1); 75 | #64 = Call(add, #63, $1); 76 | #65 = Call(add, #64, $1); 77 | #66 = Call(add, #65, $1); 78 | #67 = Call(add, #66, $1); 79 | #68 = Call(add, #67, $1); 80 | #69 = Call(add, #68, $1); 81 | #70 = Call(add, #69, $1); 82 | #71 = Call(add, #70, $1); 83 | #72 = Call(add, #71, $1); 84 | #73 = Call(add, #72, $1); 85 | #74 = Call(add, #73, $1); 86 | #75 = Call(add, #74, $1); 87 | #76 = Call(add, #75, $1); 88 | #77 = Call(add, #76, $1); 89 | #78 = Call(add, #77, $1); 90 | #79 = Call(add, #78, $1); 91 | #80 = Call(add, #79, $1); 92 | #81 = Call(add, #80, $1); 93 | #82 = Call(add, #81, $1); 94 | #83 = Call(add, #82, $1); 95 | #84 = Call(add, #83, $1); 96 | #85 = Call(add, #84, $1); 97 | #86 = Call(add, #85, $1); 98 | #87 = Call(add, #86, $1); 99 | #88 = Call(add, #87, $1); 100 | #89 = Call(add, #88, $1); 101 | #90 = Call(add, #89, $1); 102 | #91 = Call(add, #90, $1); 103 | #92 = Call(add, #91, $1); 104 | #93 = Call(add, #92, $1); 105 | #94 = Call(add, #93, $1); 106 | #95 = Call(add, #94, $1); 107 | #96 = Call(add, #95, $1); 108 | #97 = Call(add, #96, $1); 109 | #98 = Call(add, #97, $1); 110 | #99 = Call(add, #98, $1); 111 | #100 = Call(add, #99, $1); 112 | #101 = Call(add, #100, $1); 113 | ReturnValue #101; 114 | 115 | } 116 | -------------------------------------------------------------------------------- /lib/Builder/FunctionBuilder.php: -------------------------------------------------------------------------------- 1 | function = $function; 22 | } 23 | 24 | public function createBlock(string $name): BlockBuilder { 25 | if (!$this->function instanceof Function_\Implemented) { 26 | throw new \LogicException("Cannot create blocks on imported functions"); 27 | } 28 | $block = $this->function->createBlock($name); 29 | $blockBuilder = new BlockBuilder($this->context, $this, $block); 30 | $this->blocks[] = $blockBuilder; 31 | return $blockBuilder; 32 | } 33 | 34 | public function arg(int $index): Value { 35 | if (!$this->function instanceof Function_\Implemented) { 36 | throw new \LogicException("Cannot extract args on imported functions"); 37 | } 38 | return $this->function->parameters[$index]->value; 39 | } 40 | 41 | public function createLocal(string $name, Type $type): Value { 42 | if (!$this->function instanceof Function_\Implemented) { 43 | throw new \LogicException("Cannot create locals on imported functions"); 44 | } 45 | $local = new Value\Local($this->function, $name, $type); 46 | $this->function->locals[] = $local; 47 | return $local; 48 | } 49 | 50 | public function finish(): void { 51 | foreach ($this->blocks as $block) { 52 | $block->finish(); 53 | } 54 | // Handle block args 55 | // Build block graph 56 | do { 57 | $rerun = false; 58 | $parentMap = new SplObjectStorage; 59 | $argumentMap = new SplObjectStorage; 60 | foreach ($this->blocks as $block) { 61 | $argumentMap[$block->block] = $block->arguments; 62 | $block->arguments = new SplObjectStorage; 63 | foreach ($block->getTargetBlocks() as $subBlock) { 64 | if (!$parentMap->contains($subBlock)) { 65 | $parentMap[$subBlock] = []; 66 | } 67 | $a = $parentMap[$subBlock]; 68 | $a[] = $block->block; 69 | $parentMap[$subBlock] = $a; 70 | } 71 | } 72 | foreach ($parentMap as $block) { 73 | if ($argumentMap[$block]->count() === 0) { 74 | continue; 75 | } 76 | $parents = $parentMap[$block]; 77 | // for each parent, wire in arguments 78 | foreach ($parents as $parent) { 79 | $call = $parent->getBlockCallForBlock($block); 80 | if (count($call->arguments) !== count($block->arguments)) { 81 | throw new \RuntimeException("Mismatch between argument counts for block and call"); 82 | } 83 | foreach ($argumentMap[$block] as $argument) { 84 | if ($argument->isOwnedBy($parent)) { 85 | // add directly 86 | $call->arguments[] = $argument; 87 | } else { 88 | // hoist to arg 89 | $call->arguments[] = $this->findBuilderForBlock($parent)->hoistToArg($argument); 90 | $rerun = true; 91 | } 92 | } 93 | } 94 | // finally, add the positional arguments 95 | foreach ($argumentMap[$block] as $argument) { 96 | $block->arguments[] = $argumentMap[$block][$argument]; 97 | } 98 | } 99 | } while ($rerun); 100 | } 101 | 102 | private function findBuilderForBlock(Block $block): BlockBuilder { 103 | foreach ($this->blocks as $tmp) { 104 | if ($tmp->block === $block) { 105 | return $tmp; 106 | } 107 | } 108 | throw new \LogicException("Could not find block"); 109 | } 110 | } -------------------------------------------------------------------------------- /examples/common.php: -------------------------------------------------------------------------------- 1 | true, 12 | "libgccjit" => true, 13 | "llvm" => true, 14 | "php" => true, 15 | ]; 16 | } 17 | 18 | function compile(Context $context, StdClass $compilerSet): array { 19 | $libjit = new Backend\LIBJIT; 20 | $libgccjit = new Backend\LIBGCCJIT; 21 | $llvm = new Backend\LLVM; 22 | $php = new Backend\PHP; 23 | 24 | $timers = []; 25 | 26 | $results = []; 27 | $start = microtime(true); 28 | 29 | if ($compilerSet->libjit) { 30 | $results['libjit'] = $libjit->compile($context, Backend::O3); 31 | $timers["libjit"] = microtime(true); 32 | } 33 | if ($compilerSet->libgccjit) { 34 | $results['libgccjit'] = $libgccjit->compile($context, Backend::O3); 35 | $timers['libgccjit'] = microtime(true); 36 | } 37 | if ($compilerSet->llvm) { 38 | $results['llvm'] = $llvm->compile($context, Backend::O3); 39 | $timers['llvm'] = microtime(true); 40 | } 41 | 42 | if ($compilerSet->php) { 43 | $results['php'] = $php->compile($context, Backend::O3); 44 | $timers['php'] = microtime(true); 45 | } 46 | 47 | echo "Time to Compile: \n"; 48 | foreach ($timers as $name => $time) { 49 | printf("%10s: %2.6F seconds\n", $name, $time - $start); 50 | $start = $time; 51 | } 52 | echo "\n"; 53 | return $results; 54 | } 55 | 56 | function writeResultsFiles(Context $context, array $results, string $dir): void { 57 | file_put_contents($dir . '/example.ir', (new Printer)->print($context)); 58 | foreach ($results as $name => $result) { 59 | $result->dumpToFile($dir . '/' . $name); 60 | $result->dumpCompiledToFile($dir . '/' . $name); 61 | } 62 | } 63 | 64 | function getCallbacks(array $results, string $cbname): array { 65 | $callbacks = []; 66 | foreach ($results as $name => $result) { 67 | $callbacks[$name] = $result->getCallable($cbname); 68 | } 69 | return $callbacks; 70 | } 71 | 72 | function testCall(array $callbacks, string $cbname, array ...$argSets): void { 73 | echo "\nTesting $cbname:\n"; 74 | foreach ($callbacks as $compiler => $callback) { 75 | echo " Compiler $compiler\n"; 76 | foreach ($argSets as $args) { 77 | echo " " . renderCall($cbname, $args) . " = " . doCall($callback, $args) . "\n"; 78 | } 79 | } 80 | echo "\n"; 81 | } 82 | 83 | function renderCall(string $cbname, array $args): string { 84 | $params = []; 85 | foreach ($args as $arg) { 86 | if (is_array($arg)) { 87 | $params[] = renderCall($cbname, $arg); 88 | } else { 89 | $params[] = $arg; 90 | } 91 | } 92 | return $cbname . '(' . implode(', ', $params) . ')'; 93 | } 94 | 95 | function doCall(callable $cb, array $args) { 96 | $params = []; 97 | foreach ($args as $arg) { 98 | if (is_array($arg)) { 99 | $params[] = doCall($cb, $arg); 100 | } else { 101 | $params[] = $arg; 102 | } 103 | } 104 | return $cb(...$params); 105 | } 106 | 107 | function benchmark(array $callbacks, callable $closureBaseline, int $times, array $args) { 108 | echo "Benchmarking\n"; 109 | $timers = []; 110 | $start = microtime(true); 111 | foreach ($callbacks as $compiler => $callable) { 112 | for ($i = 0; $i < $times; $i++) { 113 | $callable(...$args); 114 | } 115 | $timers[$compiler] = microtime(true); 116 | } 117 | for ($i = 0; $i < $times; $i++) { 118 | $closureBaseline(...$args); 119 | } 120 | $timers["php closure"] = microtime(true); 121 | 122 | echo "Done\n\nBenchmark Results:\n"; 123 | foreach ($timers as $compiler => $time) { 124 | printf(" %12s: %2.5F seconds\n", $compiler, $time - $start); 125 | $start = $time; 126 | } 127 | echo "\n"; 128 | } 129 | 130 | function generateResults(Context $context, string $dir, string $fnName, array $tests, int $iterations, callable $baseline, ?StdClass $compilerSet = null) { 131 | $compilerSet = $compilerSet ?? getCompilerSet(); 132 | $results = compile($context, $compilerSet); 133 | writeResultsFiles($context, $results, $dir); 134 | $callbacks = getCallbacks($results, $fnName); 135 | 136 | testCall($callbacks, $fnName, ...$tests); 137 | 138 | benchmark($callbacks, $baseline, $iterations, $tests[0]); 139 | } -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/example.php: -------------------------------------------------------------------------------- 1 | type()->long_long(); 15 | 16 | // First, let's create an add(a, b) { return a + b; } function to call later: 17 | 18 | $add = $builder->alwaysInlineFunction('add', $type, false, new Parameter($type, 'a'), new Parameter($type, 'b')); 19 | $main = $add->createBlock('main'); 20 | $result = $main->add($add->arg(0), $add->arg(1)); 21 | $main->returnValue($result); 22 | 23 | 24 | // Now, let's create a second function which calls add() 10 times on itself 25 | // long long add100(long long a, long long b) 26 | // a = add(a, b) 27 | // a = add(a, b) 28 | // // 100 of these 29 | // return a; 30 | // } 31 | 32 | $add100 = $builder->exportFunction('add100', $type, false, new Parameter($type, 'a'), new Parameter($type, 'b')); 33 | $main = $add100->createBlock('main'); 34 | $r1 = $add100->arg(0); 35 | for ($i = 0; $i < 100; $i++) { 36 | $r1 = $main->call($add, $r1, $add100->arg(1)); 37 | } 38 | $main->returnValue($r1); 39 | 40 | $builder->finish(); 41 | 42 | 43 | echo "Add100 Function: 44 | long long add(long long a, long long b) { 45 | return a + b; 46 | } 47 | long long add100(long long a, long long b) { 48 | a = add(a, b) 49 | a = add(a, b) 50 | // 100 of these 51 | return a; 52 | } 53 | "; 54 | 55 | function add(int $a, int $b): int { 56 | return $a + $b; 57 | } 58 | $cb = function(int $a, int $b): int { 59 | $a = add($a, $b); 60 | $a = add($a, $b); 61 | $a = add($a, $b); 62 | $a = add($a, $b); 63 | $a = add($a, $b); 64 | $a = add($a, $b); 65 | $a = add($a, $b); 66 | $a = add($a, $b); 67 | $a = add($a, $b); 68 | $a = add($a, $b); 69 | $a = add($a, $b); 70 | $a = add($a, $b); 71 | $a = add($a, $b); 72 | $a = add($a, $b); 73 | $a = add($a, $b); 74 | $a = add($a, $b); 75 | $a = add($a, $b); 76 | $a = add($a, $b); 77 | $a = add($a, $b); 78 | $a = add($a, $b); 79 | $a = add($a, $b); 80 | $a = add($a, $b); 81 | $a = add($a, $b); 82 | $a = add($a, $b); 83 | $a = add($a, $b); 84 | $a = add($a, $b); 85 | $a = add($a, $b); 86 | $a = add($a, $b); 87 | $a = add($a, $b); 88 | $a = add($a, $b); 89 | $a = add($a, $b); 90 | $a = add($a, $b); 91 | $a = add($a, $b); 92 | $a = add($a, $b); 93 | $a = add($a, $b); 94 | $a = add($a, $b); 95 | $a = add($a, $b); 96 | $a = add($a, $b); 97 | $a = add($a, $b); 98 | $a = add($a, $b); 99 | $a = add($a, $b); 100 | $a = add($a, $b); 101 | $a = add($a, $b); 102 | $a = add($a, $b); 103 | $a = add($a, $b); 104 | $a = add($a, $b); 105 | $a = add($a, $b); 106 | $a = add($a, $b); 107 | $a = add($a, $b); 108 | $a = add($a, $b); 109 | $a = add($a, $b); 110 | $a = add($a, $b); 111 | $a = add($a, $b); 112 | $a = add($a, $b); 113 | $a = add($a, $b); 114 | $a = add($a, $b); 115 | $a = add($a, $b); 116 | $a = add($a, $b); 117 | $a = add($a, $b); 118 | $a = add($a, $b); 119 | $a = add($a, $b); 120 | $a = add($a, $b); 121 | $a = add($a, $b); 122 | $a = add($a, $b); 123 | $a = add($a, $b); 124 | $a = add($a, $b); 125 | $a = add($a, $b); 126 | $a = add($a, $b); 127 | $a = add($a, $b); 128 | $a = add($a, $b); 129 | $a = add($a, $b); 130 | $a = add($a, $b); 131 | $a = add($a, $b); 132 | $a = add($a, $b); 133 | $a = add($a, $b); 134 | $a = add($a, $b); 135 | $a = add($a, $b); 136 | $a = add($a, $b); 137 | $a = add($a, $b); 138 | $a = add($a, $b); 139 | $a = add($a, $b); 140 | $a = add($a, $b); 141 | $a = add($a, $b); 142 | $a = add($a, $b); 143 | $a = add($a, $b); 144 | $a = add($a, $b); 145 | $a = add($a, $b); 146 | $a = add($a, $b); 147 | $a = add($a, $b); 148 | $a = add($a, $b); 149 | $a = add($a, $b); 150 | $a = add($a, $b); 151 | $a = add($a, $b); 152 | $a = add($a, $b); 153 | $a = add($a, $b); 154 | $a = add($a, $b); 155 | $a = add($a, $b); 156 | $a = add($a, $b); 157 | $a = add($a, $b); 158 | $a = add($a, $b); 159 | return $a; 160 | }; 161 | 162 | 163 | generateResults( 164 | $context, 165 | // current directory 166 | __DIR__, 167 | // fn name 168 | 'add100', 169 | // tests 170 | [[1, 1], [1, 2], [99, 1], [[1, 2], 3]], 171 | // benchmark iterations 172 | 1000000, 173 | // baseline implementation 174 | $cb 175 | ); -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/llvm.bc: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'test_0' 2 | source_filename = "test_0" 3 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 4 | 5 | define i64 @add(i64, i64) #0 { 6 | main: 7 | %var_0 = add i64 %0, %1 8 | ret i64 %var_0 9 | } 10 | 11 | define i64 @add100(i64, i64) { 12 | main: 13 | %add = call i64 @add(i64 %0, i64 %1) 14 | %add1 = call i64 @add(i64 %add, i64 %1) 15 | %add2 = call i64 @add(i64 %add1, i64 %1) 16 | %add3 = call i64 @add(i64 %add2, i64 %1) 17 | %add4 = call i64 @add(i64 %add3, i64 %1) 18 | %add5 = call i64 @add(i64 %add4, i64 %1) 19 | %add6 = call i64 @add(i64 %add5, i64 %1) 20 | %add7 = call i64 @add(i64 %add6, i64 %1) 21 | %add8 = call i64 @add(i64 %add7, i64 %1) 22 | %add9 = call i64 @add(i64 %add8, i64 %1) 23 | %add10 = call i64 @add(i64 %add9, i64 %1) 24 | %add11 = call i64 @add(i64 %add10, i64 %1) 25 | %add12 = call i64 @add(i64 %add11, i64 %1) 26 | %add13 = call i64 @add(i64 %add12, i64 %1) 27 | %add14 = call i64 @add(i64 %add13, i64 %1) 28 | %add15 = call i64 @add(i64 %add14, i64 %1) 29 | %add16 = call i64 @add(i64 %add15, i64 %1) 30 | %add17 = call i64 @add(i64 %add16, i64 %1) 31 | %add18 = call i64 @add(i64 %add17, i64 %1) 32 | %add19 = call i64 @add(i64 %add18, i64 %1) 33 | %add20 = call i64 @add(i64 %add19, i64 %1) 34 | %add21 = call i64 @add(i64 %add20, i64 %1) 35 | %add22 = call i64 @add(i64 %add21, i64 %1) 36 | %add23 = call i64 @add(i64 %add22, i64 %1) 37 | %add24 = call i64 @add(i64 %add23, i64 %1) 38 | %add25 = call i64 @add(i64 %add24, i64 %1) 39 | %add26 = call i64 @add(i64 %add25, i64 %1) 40 | %add27 = call i64 @add(i64 %add26, i64 %1) 41 | %add28 = call i64 @add(i64 %add27, i64 %1) 42 | %add29 = call i64 @add(i64 %add28, i64 %1) 43 | %add30 = call i64 @add(i64 %add29, i64 %1) 44 | %add31 = call i64 @add(i64 %add30, i64 %1) 45 | %add32 = call i64 @add(i64 %add31, i64 %1) 46 | %add33 = call i64 @add(i64 %add32, i64 %1) 47 | %add34 = call i64 @add(i64 %add33, i64 %1) 48 | %add35 = call i64 @add(i64 %add34, i64 %1) 49 | %add36 = call i64 @add(i64 %add35, i64 %1) 50 | %add37 = call i64 @add(i64 %add36, i64 %1) 51 | %add38 = call i64 @add(i64 %add37, i64 %1) 52 | %add39 = call i64 @add(i64 %add38, i64 %1) 53 | %add40 = call i64 @add(i64 %add39, i64 %1) 54 | %add41 = call i64 @add(i64 %add40, i64 %1) 55 | %add42 = call i64 @add(i64 %add41, i64 %1) 56 | %add43 = call i64 @add(i64 %add42, i64 %1) 57 | %add44 = call i64 @add(i64 %add43, i64 %1) 58 | %add45 = call i64 @add(i64 %add44, i64 %1) 59 | %add46 = call i64 @add(i64 %add45, i64 %1) 60 | %add47 = call i64 @add(i64 %add46, i64 %1) 61 | %add48 = call i64 @add(i64 %add47, i64 %1) 62 | %add49 = call i64 @add(i64 %add48, i64 %1) 63 | %add50 = call i64 @add(i64 %add49, i64 %1) 64 | %add51 = call i64 @add(i64 %add50, i64 %1) 65 | %add52 = call i64 @add(i64 %add51, i64 %1) 66 | %add53 = call i64 @add(i64 %add52, i64 %1) 67 | %add54 = call i64 @add(i64 %add53, i64 %1) 68 | %add55 = call i64 @add(i64 %add54, i64 %1) 69 | %add56 = call i64 @add(i64 %add55, i64 %1) 70 | %add57 = call i64 @add(i64 %add56, i64 %1) 71 | %add58 = call i64 @add(i64 %add57, i64 %1) 72 | %add59 = call i64 @add(i64 %add58, i64 %1) 73 | %add60 = call i64 @add(i64 %add59, i64 %1) 74 | %add61 = call i64 @add(i64 %add60, i64 %1) 75 | %add62 = call i64 @add(i64 %add61, i64 %1) 76 | %add63 = call i64 @add(i64 %add62, i64 %1) 77 | %add64 = call i64 @add(i64 %add63, i64 %1) 78 | %add65 = call i64 @add(i64 %add64, i64 %1) 79 | %add66 = call i64 @add(i64 %add65, i64 %1) 80 | %add67 = call i64 @add(i64 %add66, i64 %1) 81 | %add68 = call i64 @add(i64 %add67, i64 %1) 82 | %add69 = call i64 @add(i64 %add68, i64 %1) 83 | %add70 = call i64 @add(i64 %add69, i64 %1) 84 | %add71 = call i64 @add(i64 %add70, i64 %1) 85 | %add72 = call i64 @add(i64 %add71, i64 %1) 86 | %add73 = call i64 @add(i64 %add72, i64 %1) 87 | %add74 = call i64 @add(i64 %add73, i64 %1) 88 | %add75 = call i64 @add(i64 %add74, i64 %1) 89 | %add76 = call i64 @add(i64 %add75, i64 %1) 90 | %add77 = call i64 @add(i64 %add76, i64 %1) 91 | %add78 = call i64 @add(i64 %add77, i64 %1) 92 | %add79 = call i64 @add(i64 %add78, i64 %1) 93 | %add80 = call i64 @add(i64 %add79, i64 %1) 94 | %add81 = call i64 @add(i64 %add80, i64 %1) 95 | %add82 = call i64 @add(i64 %add81, i64 %1) 96 | %add83 = call i64 @add(i64 %add82, i64 %1) 97 | %add84 = call i64 @add(i64 %add83, i64 %1) 98 | %add85 = call i64 @add(i64 %add84, i64 %1) 99 | %add86 = call i64 @add(i64 %add85, i64 %1) 100 | %add87 = call i64 @add(i64 %add86, i64 %1) 101 | %add88 = call i64 @add(i64 %add87, i64 %1) 102 | %add89 = call i64 @add(i64 %add88, i64 %1) 103 | %add90 = call i64 @add(i64 %add89, i64 %1) 104 | %add91 = call i64 @add(i64 %add90, i64 %1) 105 | %add92 = call i64 @add(i64 %add91, i64 %1) 106 | %add93 = call i64 @add(i64 %add92, i64 %1) 107 | %add94 = call i64 @add(i64 %add93, i64 %1) 108 | %add95 = call i64 @add(i64 %add94, i64 %1) 109 | %add96 = call i64 @add(i64 %add95, i64 %1) 110 | %add97 = call i64 @add(i64 %add96, i64 %1) 111 | %add98 = call i64 @add(i64 %add97, i64 %1) 112 | %add99 = call i64 @add(i64 %add98, i64 %1) 113 | ret i64 %add99 114 | } 115 | 116 | attributes #0 = { "alwaysinline" } 117 | -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/llvm.s: -------------------------------------------------------------------------------- 1 | .text 2 | .file "test_0" 3 | .globl add 4 | .p2align 4, 0x90 5 | .type add,@function 6 | add: 7 | .cfi_startproc 8 | leaq (%rdi,%rsi), %rax 9 | retq 10 | .Lfunc_end0: 11 | .size add, .Lfunc_end0-add 12 | .cfi_endproc 13 | 14 | .globl add100 15 | .p2align 4, 0x90 16 | .type add100,@function 17 | add100: 18 | .cfi_startproc 19 | pushq %r14 20 | .Lcfi0: 21 | .cfi_def_cfa_offset 16 22 | pushq %rbx 23 | .Lcfi1: 24 | .cfi_def_cfa_offset 24 25 | pushq %rax 26 | .Lcfi2: 27 | .cfi_def_cfa_offset 32 28 | .Lcfi3: 29 | .cfi_offset %rbx, -24 30 | .Lcfi4: 31 | .cfi_offset %r14, -16 32 | movq %rsi, %rbx 33 | movabsq $add, %r14 34 | callq *%r14 35 | movq %rax, %rdi 36 | movq %rbx, %rsi 37 | callq *%r14 38 | movq %rax, %rdi 39 | movq %rbx, %rsi 40 | callq *%r14 41 | movq %rax, %rdi 42 | movq %rbx, %rsi 43 | callq *%r14 44 | movq %rax, %rdi 45 | movq %rbx, %rsi 46 | callq *%r14 47 | movq %rax, %rdi 48 | movq %rbx, %rsi 49 | callq *%r14 50 | movq %rax, %rdi 51 | movq %rbx, %rsi 52 | callq *%r14 53 | movq %rax, %rdi 54 | movq %rbx, %rsi 55 | callq *%r14 56 | movq %rax, %rdi 57 | movq %rbx, %rsi 58 | callq *%r14 59 | movq %rax, %rdi 60 | movq %rbx, %rsi 61 | callq *%r14 62 | movq %rax, %rdi 63 | movq %rbx, %rsi 64 | callq *%r14 65 | movq %rax, %rdi 66 | movq %rbx, %rsi 67 | callq *%r14 68 | movq %rax, %rdi 69 | movq %rbx, %rsi 70 | callq *%r14 71 | movq %rax, %rdi 72 | movq %rbx, %rsi 73 | callq *%r14 74 | movq %rax, %rdi 75 | movq %rbx, %rsi 76 | callq *%r14 77 | movq %rax, %rdi 78 | movq %rbx, %rsi 79 | callq *%r14 80 | movq %rax, %rdi 81 | movq %rbx, %rsi 82 | callq *%r14 83 | movq %rax, %rdi 84 | movq %rbx, %rsi 85 | callq *%r14 86 | movq %rax, %rdi 87 | movq %rbx, %rsi 88 | callq *%r14 89 | movq %rax, %rdi 90 | movq %rbx, %rsi 91 | callq *%r14 92 | movq %rax, %rdi 93 | movq %rbx, %rsi 94 | callq *%r14 95 | movq %rax, %rdi 96 | movq %rbx, %rsi 97 | callq *%r14 98 | movq %rax, %rdi 99 | movq %rbx, %rsi 100 | callq *%r14 101 | movq %rax, %rdi 102 | movq %rbx, %rsi 103 | callq *%r14 104 | movq %rax, %rdi 105 | movq %rbx, %rsi 106 | callq *%r14 107 | movq %rax, %rdi 108 | movq %rbx, %rsi 109 | callq *%r14 110 | movq %rax, %rdi 111 | movq %rbx, %rsi 112 | callq *%r14 113 | movq %rax, %rdi 114 | movq %rbx, %rsi 115 | callq *%r14 116 | movq %rax, %rdi 117 | movq %rbx, %rsi 118 | callq *%r14 119 | movq %rax, %rdi 120 | movq %rbx, %rsi 121 | callq *%r14 122 | movq %rax, %rdi 123 | movq %rbx, %rsi 124 | callq *%r14 125 | movq %rax, %rdi 126 | movq %rbx, %rsi 127 | callq *%r14 128 | movq %rax, %rdi 129 | movq %rbx, %rsi 130 | callq *%r14 131 | movq %rax, %rdi 132 | movq %rbx, %rsi 133 | callq *%r14 134 | movq %rax, %rdi 135 | movq %rbx, %rsi 136 | callq *%r14 137 | movq %rax, %rdi 138 | movq %rbx, %rsi 139 | callq *%r14 140 | movq %rax, %rdi 141 | movq %rbx, %rsi 142 | callq *%r14 143 | movq %rax, %rdi 144 | movq %rbx, %rsi 145 | callq *%r14 146 | movq %rax, %rdi 147 | movq %rbx, %rsi 148 | callq *%r14 149 | movq %rax, %rdi 150 | movq %rbx, %rsi 151 | callq *%r14 152 | movq %rax, %rdi 153 | movq %rbx, %rsi 154 | callq *%r14 155 | movq %rax, %rdi 156 | movq %rbx, %rsi 157 | callq *%r14 158 | movq %rax, %rdi 159 | movq %rbx, %rsi 160 | callq *%r14 161 | movq %rax, %rdi 162 | movq %rbx, %rsi 163 | callq *%r14 164 | movq %rax, %rdi 165 | movq %rbx, %rsi 166 | callq *%r14 167 | movq %rax, %rdi 168 | movq %rbx, %rsi 169 | callq *%r14 170 | movq %rax, %rdi 171 | movq %rbx, %rsi 172 | callq *%r14 173 | movq %rax, %rdi 174 | movq %rbx, %rsi 175 | callq *%r14 176 | movq %rax, %rdi 177 | movq %rbx, %rsi 178 | callq *%r14 179 | movq %rax, %rdi 180 | movq %rbx, %rsi 181 | callq *%r14 182 | movq %rax, %rdi 183 | movq %rbx, %rsi 184 | callq *%r14 185 | movq %rax, %rdi 186 | movq %rbx, %rsi 187 | callq *%r14 188 | movq %rax, %rdi 189 | movq %rbx, %rsi 190 | callq *%r14 191 | movq %rax, %rdi 192 | movq %rbx, %rsi 193 | callq *%r14 194 | movq %rax, %rdi 195 | movq %rbx, %rsi 196 | callq *%r14 197 | movq %rax, %rdi 198 | movq %rbx, %rsi 199 | callq *%r14 200 | movq %rax, %rdi 201 | movq %rbx, %rsi 202 | callq *%r14 203 | movq %rax, %rdi 204 | movq %rbx, %rsi 205 | callq *%r14 206 | movq %rax, %rdi 207 | movq %rbx, %rsi 208 | callq *%r14 209 | movq %rax, %rdi 210 | movq %rbx, %rsi 211 | callq *%r14 212 | movq %rax, %rdi 213 | movq %rbx, %rsi 214 | callq *%r14 215 | movq %rax, %rdi 216 | movq %rbx, %rsi 217 | callq *%r14 218 | movq %rax, %rdi 219 | movq %rbx, %rsi 220 | callq *%r14 221 | movq %rax, %rdi 222 | movq %rbx, %rsi 223 | callq *%r14 224 | movq %rax, %rdi 225 | movq %rbx, %rsi 226 | callq *%r14 227 | movq %rax, %rdi 228 | movq %rbx, %rsi 229 | callq *%r14 230 | movq %rax, %rdi 231 | movq %rbx, %rsi 232 | callq *%r14 233 | movq %rax, %rdi 234 | movq %rbx, %rsi 235 | callq *%r14 236 | movq %rax, %rdi 237 | movq %rbx, %rsi 238 | callq *%r14 239 | movq %rax, %rdi 240 | movq %rbx, %rsi 241 | callq *%r14 242 | movq %rax, %rdi 243 | movq %rbx, %rsi 244 | callq *%r14 245 | movq %rax, %rdi 246 | movq %rbx, %rsi 247 | callq *%r14 248 | movq %rax, %rdi 249 | movq %rbx, %rsi 250 | callq *%r14 251 | movq %rax, %rdi 252 | movq %rbx, %rsi 253 | callq *%r14 254 | movq %rax, %rdi 255 | movq %rbx, %rsi 256 | callq *%r14 257 | movq %rax, %rdi 258 | movq %rbx, %rsi 259 | callq *%r14 260 | movq %rax, %rdi 261 | movq %rbx, %rsi 262 | callq *%r14 263 | movq %rax, %rdi 264 | movq %rbx, %rsi 265 | callq *%r14 266 | movq %rax, %rdi 267 | movq %rbx, %rsi 268 | callq *%r14 269 | movq %rax, %rdi 270 | movq %rbx, %rsi 271 | callq *%r14 272 | movq %rax, %rdi 273 | movq %rbx, %rsi 274 | callq *%r14 275 | movq %rax, %rdi 276 | movq %rbx, %rsi 277 | callq *%r14 278 | movq %rax, %rdi 279 | movq %rbx, %rsi 280 | callq *%r14 281 | movq %rax, %rdi 282 | movq %rbx, %rsi 283 | callq *%r14 284 | movq %rax, %rdi 285 | movq %rbx, %rsi 286 | callq *%r14 287 | movq %rax, %rdi 288 | movq %rbx, %rsi 289 | callq *%r14 290 | movq %rax, %rdi 291 | movq %rbx, %rsi 292 | callq *%r14 293 | movq %rax, %rdi 294 | movq %rbx, %rsi 295 | callq *%r14 296 | movq %rax, %rdi 297 | movq %rbx, %rsi 298 | callq *%r14 299 | movq %rax, %rdi 300 | movq %rbx, %rsi 301 | callq *%r14 302 | movq %rax, %rdi 303 | movq %rbx, %rsi 304 | callq *%r14 305 | movq %rax, %rdi 306 | movq %rbx, %rsi 307 | callq *%r14 308 | movq %rax, %rdi 309 | movq %rbx, %rsi 310 | callq *%r14 311 | movq %rax, %rdi 312 | movq %rbx, %rsi 313 | callq *%r14 314 | movq %rax, %rdi 315 | movq %rbx, %rsi 316 | callq *%r14 317 | movq %rax, %rdi 318 | movq %rbx, %rsi 319 | callq *%r14 320 | movq %rax, %rdi 321 | movq %rbx, %rsi 322 | callq *%r14 323 | movq %rax, %rdi 324 | movq %rbx, %rsi 325 | callq *%r14 326 | movq %rax, %rdi 327 | movq %rbx, %rsi 328 | callq *%r14 329 | movq %rax, %rdi 330 | movq %rbx, %rsi 331 | callq *%r14 332 | addq $8, %rsp 333 | popq %rbx 334 | popq %r14 335 | retq 336 | .Lfunc_end1: 337 | .size add100, .Lfunc_end1-add100 338 | .cfi_endproc 339 | 340 | 341 | .section ".note.GNU-stack","",@progbits 342 | -------------------------------------------------------------------------------- /lib/Printer.php: -------------------------------------------------------------------------------- 1 | types as $type) { 15 | $text = $this->printType($type, self::PRINT_MODE_FULL); 16 | if (!empty($text)) { 17 | $results[] = $text; 18 | } 19 | } 20 | $constantMap = new SplObjectStorage; 21 | foreach ($context->constants as $constant) { 22 | $constantMap[$constant] = '(' . $this->printType($constant->type) . ') ' . $constant->value; 23 | } 24 | foreach ($context->imports as $import) { 25 | $results[] = "external " . $this->printFunctionDeclaration($import, new SplObjectStorage, 0) . ';'; 26 | } 27 | $results[] = ''; 28 | foreach ($context->functions as $function) { 29 | $results[] = $this->printFunction($function, $constantMap); 30 | } 31 | return implode("\n", $results); 32 | } 33 | 34 | public function printFunctionDeclaration(IR\Function_ $function, SplObjectStorage $scope, int $constantOffset) { 35 | $return = $this->printType($function->returnType) . ' ' . $function->name . '('; 36 | $params = []; 37 | foreach ($function->parameters as $param) { 38 | if ($function instanceof IR\Function_\Implemented) { 39 | $scope[$param->value] = '$' . (count($scope) - $constantOffset); 40 | $params[] = $this->printType($param->type) . ' ' . $scope[$param->value] . "<{$param->name}>"; 41 | } else { 42 | $params[] = $this->printType($param->type) . ' ' . $param->name; 43 | } 44 | } 45 | if ($function->isVariadic) { 46 | $params[] = "..."; 47 | } 48 | $return .= implode(', ', $params) . ")"; 49 | return $return; 50 | } 51 | 52 | public function printFunction(IR\Function_ $function, SplObjectStorage $constantMap): string { 53 | $scope = new SplObjectStorage; 54 | foreach ($constantMap as $constant) { 55 | $scope[$constant] = $constantMap[$constant]; 56 | } 57 | $constantOffset = count($constantMap); 58 | $return = $this->printFunctionDeclaration($function, $scope, $constantOffset) . " {\n"; 59 | 60 | foreach ($function->locals as $local) { 61 | $scope[$local] = '$' . (count($scope) - $constantOffset); 62 | 63 | $return .= ' ' . $this->printType($local->type) . ' ' . $scope[$local] . '<' . $local->name . ">;\n"; 64 | } 65 | $return .= "\n"; 66 | foreach ($function->blocks as $block) { 67 | $return .= ' ' . $block->name . '('; 68 | $args = []; 69 | foreach ($block->arguments as $argument) { 70 | $scope[$argument] = '#' . (count($scope) - $constantOffset); 71 | $args[] = $scope[$argument]; 72 | } 73 | $return .= implode(', ', $args) . "):\n"; 74 | foreach ($block->ops as $op) { 75 | if ($op instanceof IR\TerminalOp) { 76 | if ($op instanceof IR\Op\ReturnVoid) { 77 | $return .= " Return;\n"; 78 | } elseif ($op instanceof IR\Op\ReturnValue) { 79 | $return .= ' ReturnValue ' . $scope[$op->value] . ";\n"; 80 | } elseif ($op instanceof IR\Op\BlockCall) { 81 | $return .= ' ' . $this->printBlockCall($op); 82 | } elseif ($op instanceof IR\Op\ConditionalBlockCall) { 83 | $return .= ' IF (' . $scope[$op->cond] . "):\n"; 84 | $return .= ' ' . $this->printBlockCall($op->ifTrue, $scope); 85 | $return .= " ELSE:\n"; 86 | $return .= ' ' . $this->printBlockCall($op->ifFalse, $scope); 87 | } else { 88 | throw new \LogicException("Unknown terminal op found: " . get_class($op)); 89 | } 90 | } elseif ($op instanceof IR\Op\FieldRead) { 91 | $scope[$op->return] = '#' . (count($scope) - $constantOffset); 92 | $return .= ' ' . $scope[$op->return] . ' = ' . $scope[$op->struct] . '.' . $op->field->name . ";\n"; 93 | } elseif ($op instanceof IR\Op\FieldWrite) { 94 | $return .= ' ' . $scope[$op->struct] . '.' . $op->field->name . ' = ' . $scope[$op->value] . ";\n"; 95 | } else { 96 | $tmp = $op->getResult(); 97 | if ($tmp) { 98 | $scope[$tmp] = '#' . (count($scope) - $constantOffset); 99 | $return .= ' ' . $scope[$tmp] . ' = '; 100 | } else { 101 | $return .= ' '; 102 | } 103 | $return .= $op->getShortName() . "("; 104 | $args = []; 105 | if ($op instanceof IR\Op\Call) { 106 | $args[] = $op->function->name; 107 | } elseif ($op instanceof IR\Op\CallNoReturn) { 108 | $args[] = $op->function->name; 109 | } 110 | foreach ($op->getArguments() as $idx => $argument) { 111 | $args[] = $scope[$argument]; 112 | } 113 | $return .= implode(', ', $args) . ");\n"; 114 | } 115 | } 116 | $return .= "\n"; 117 | } 118 | return $return . "}\n"; 119 | } 120 | 121 | protected function printBlockCall(IR\Op\BlockCall $op, SplObjectStorage $scope): string { 122 | $return = 'jump ' . $op->block->name . '('; 123 | $args = []; 124 | foreach ($op->arguments as $arg) { 125 | $args[] = $scope[$arg]; 126 | } 127 | $return .= implode(', ', $args) . ");\n"; 128 | return $return; 129 | } 130 | 131 | public function printType(Type $type, int $mode = self::PRINT_MODE_NORMAL): string { 132 | if ($mode & self::PRINT_MODE_FULL) { 133 | if ($type instanceof Type\Struct) { 134 | $return = "struct {$type->name} {\n"; 135 | foreach ($type->fields as $field) { 136 | $return .= " " . $this->printType($field->type, self::PRINT_MODE_ALWAYS_FULL) . " {$field->name};\n"; 137 | } 138 | return $return . "}"; 139 | } 140 | } 141 | if ($mode === self::PRINT_MODE_FULL) { 142 | // skip 143 | return ''; 144 | } 145 | if ($type instanceof Type\Primitive) { 146 | return $type->asCString(); 147 | } elseif ($type instanceof Type\Const_) { 148 | return 'const ' . $this->printType($type->parent, $mode); 149 | } elseif ($type instanceof Type\Pointer) { 150 | return '(' . $this->printType($type->parent, $mode) . ')*'; 151 | } elseif ($type instanceof Type\Volatile) { 152 | return 'volatile ' . $this->printType($type->parent, $mode); 153 | } elseif ($type instanceof Type\Struct) { 154 | return 'struct ' . $type->name; 155 | } elseif ($type instanceof Type\ArrayType) { 156 | return '(' . $this->printType($type->parent, $mode) . ')[' . $type->numElements . ']'; 157 | } 158 | } 159 | 160 | } -------------------------------------------------------------------------------- /lib/Backend/PHP.php: -------------------------------------------------------------------------------- 1 | 'void', 30 | Type\Primitive::T_VOID_PTR => '', 31 | Type\Primitive::T_BOOL => 'bool', 32 | Type\Primitive::T_CHAR => 'int', 33 | Type\Primitive::T_SIGNED_CHAR => 'int', 34 | Type\Primitive::T_UNSIGNED_CHAR => 'int', 35 | Type\Primitive::T_SHORT => 'int', 36 | Type\Primitive::T_UNSIGNED_SHORT => 'int', 37 | Type\Primitive::T_INT => 'int', 38 | Type\Primitive::T_UNSIGNED_INT => 'int', 39 | Type\Primitive::T_LONG => 'int', 40 | Type\Primitive::T_UNSIGNED_LONG => 'int', 41 | Type\Primitive::T_LONG_LONG => 'int', 42 | Type\Primitive::T_UNSIGNED_LONG_LONG => 'int', 43 | Type\Primitive::T_FLOAT => 'float', 44 | Type\Primitive::T_DOUBLE => 'float', 45 | Type\Primitive::T_LONG_DOUBLE => 'float', 46 | Type\Primitive::T_SIZE_T => 'int', 47 | ]; 48 | 49 | public function __construct() { 50 | } 51 | 52 | private string $namespace; 53 | 54 | private string $code; 55 | 56 | protected function beforeCompile(Context $context, int $optimizationLevel): void { 57 | $this->parameterMap = []; 58 | $this->fieldMap = new SplObjectStorage; 59 | $this->structMap = new SplObjectStorage; 60 | $this->namespace = 'ct_' . mt_rand(0, 99999); 61 | $this->code = 'declare(strict_types=1); namespace ' . $this->namespace . ";\n"; 62 | } 63 | 64 | protected function afterCompile(Context $context): void { 65 | unset($this->parameterMap); 66 | unset($this->fieldMap); 67 | unset($this->structMap); 68 | unset($this->namespace); 69 | unset($this->code); 70 | } 71 | 72 | protected function compileType(Type $type) { 73 | if ($type instanceof Type\Primitive) { 74 | return self::PRIMITIVE_TYPE_MAP[$type->kind]; 75 | } elseif ($type instanceof Type\Struct) { 76 | $code = 'class ' . $type->name . " {\n"; 77 | foreach ($type->fields as $field) { 78 | $code .= " public {$this->typeMap[$field->type]} \${$field->name};\n"; 79 | } 80 | $code .= "}\n"; 81 | $this->code .= $code; 82 | return '\\' . $this->namespace . '\\' . $type->name; 83 | } 84 | throw new \LogicException("Not implemented type for gcc_jit: " . get_class($type)); 85 | } 86 | 87 | protected function compileConstant(Constant $constant) { 88 | return $constant->value; 89 | } 90 | 91 | protected function importFunction(Function_ $function) { 92 | // noop 93 | } 94 | 95 | protected array $declaredFunctions = []; 96 | 97 | protected function declareFunction(Function_ $function) { 98 | // noop 99 | if ($function instanceof Function_\AlwaysInline) { 100 | } elseif ($function instanceof Function_\Static_) { 101 | } else { 102 | $this->declaredFunctions[$function->name] = $this->namespace . '\\' . $function->name; 103 | } 104 | } 105 | 106 | private SplObjectStorage $localMap; 107 | private SplObjectStorage $valueMap; 108 | private SplObjectStorage $blockMap; 109 | 110 | protected function compileFunction(Function_ $function, $func): void { 111 | $this->valueMap = new SplObjectStorage; 112 | $this->blockMap = new SplObjectStorage; 113 | $this->localMap = new SplObjectStorage; 114 | $code = 'function ' . $function->name . '('; 115 | foreach ($this->constantMap as $constant) { 116 | $this->valueMap[$constant] = $this->constantMap[$constant]; 117 | } 118 | $params = []; 119 | foreach ($function->parameters as $index => $parameter) { 120 | $this->valueMap[$parameter->value] = '$p_' . $index; 121 | $params[] = $this->typeMap[$parameter->type] . ' ' . $this->valueMap[$parameter->value]; 122 | } 123 | $code .= implode(', ', $params) . '): ' . $this->typeMap[$function->returnType] . " {\n"; 124 | foreach ($function->locals as $local) { 125 | $this->localMap[$local] = '$l_' . count($this->localMap); 126 | $this->valueMap[$local] = $this->localMap[$local]; 127 | if ($local->type instanceof Type\Struct) { 128 | $code .= " {$this->localMap[$local]} = new {$this->typeMap[$local->type]};\n"; 129 | } 130 | } 131 | foreach ($function->blocks as $block) { 132 | foreach ($block->arguments as $idx => $argument) { 133 | $this->localMap[$argument] = '$bs_' . count($this->localMap); 134 | } 135 | } 136 | foreach ($function->blocks as $block) { 137 | $code .= $this->compileBlock($block); 138 | } 139 | $code .= "}\n"; 140 | $this->code .= $code; 141 | unset($this->localMap); 142 | unset($this->valueMap); 143 | unset($this->blockMap); 144 | } 145 | 146 | protected function compileBlock(Block $block): string { 147 | $code = $block->name . ":\n"; 148 | foreach ($block->arguments as $argument) { 149 | $this->valueMap[$argument] = '$ba_' . count($this->valueMap); 150 | $code .= " {$this->valueMap[$argument]} = {$this->localMap[$argument]};\n"; 151 | } 152 | foreach ($block->ops as $op) { 153 | $code .= $this->compileOp($op); 154 | } 155 | return $code; 156 | } 157 | 158 | public function temp(Value $value): string { 159 | $this->valueMap[$value] = '$t_' . count($this->valueMap); 160 | return $this->valueMap[$value]; 161 | } 162 | 163 | protected function compileOp(Op $op): string { 164 | if ($op instanceof Op\BinaryOp) { 165 | return $this->compileBinaryOp($op); 166 | } elseif ($op instanceof Op\ReturnVoid) { 167 | return " return;\n"; 168 | } elseif ($op instanceof Op\ReturnValue) { 169 | return " return {$this->valueMap[$op->value]};\n"; 170 | } elseif ($op instanceof Op\Call) { 171 | return " " . $this->temp($op->return) . " = " . $this->doCall($op) . ";\n"; 172 | } elseif ($op instanceof Op\CallNoReturn) { 173 | return " " . $this->doCall($op) . ";\n"; 174 | } elseif ($op instanceof Op\FieldRead) { 175 | return " " . $this->temp($op->return) . " = {$this->valueMap[$op->struct]}->{$op->field->name};\n"; 176 | } elseif ($op instanceof Op\FieldWrite) { 177 | if ($this->localMap->contains($op->struct)) { 178 | return " {$this->localMap[$op->struct]}->{$op->field->name} = {$this->valueMap[$op->value]};\n"; 179 | } else { 180 | throw new \LogicException("Cannot write to rvalue"); 181 | } 182 | } elseif ($op instanceof Op\BlockCall) { 183 | return $this->compileBlockCall($op); 184 | } elseif ($op instanceof Op\ConditionalBlockCall) { 185 | return " if ({$this->valueMap[$op->cond]}) {\n" 186 | . " " . $this->compileBlockCall($op->ifTrue) . "\n" 187 | . " } else {\n" 188 | . " " . $this->compileBlockCall($op->ifFalse) . "\n" 189 | . " }\n"; 190 | } else { 191 | throw new \LogicException("Unknown Op encountered: " . get_class($op)); 192 | } 193 | } 194 | 195 | protected function doCall(Op $op): string { 196 | $params = []; 197 | foreach ($op->parameters as $parameter) { 198 | $params[] = $this->valueMap[$parameter]; 199 | } 200 | return $op->function->name . '(' . implode(', ', $params) . ')'; 201 | } 202 | 203 | protected function compileBlockCall(Op\BlockCall $op): string { 204 | $code = ''; 205 | foreach ($op->block->arguments as $index => $argument) { 206 | $code .= " {$this->localMap[$argument]} = {$this->valueMap[$op->arguments[$index]]};\n"; 207 | } 208 | $code .= " goto {$op->block->name};\n"; 209 | return $code; 210 | } 211 | 212 | const BINARYOP_MAP = [ 213 | Op\BinaryOp\Add::class => '+', 214 | Op\BinaryOp\BitwiseAnd::class => '&', 215 | Op\BinaryOp\BitwiseOr::class => '|', 216 | Op\BinaryOp\BitwiseXor::class => '^', 217 | Op\BinaryOp\Div::class => '/', 218 | Op\BinaryOp\LogicalAnd::class => '&&', 219 | Op\BinaryOp\LogicalOr::class => '||', 220 | Op\BinaryOp\Mod::class => '%', 221 | Op\BinaryOp\Mul::class => '*', 222 | Op\BinaryOp\SL::class => '<<', 223 | Op\BinaryOp\SR::class => '>>', 224 | Op\BinaryOp\Sub::class => '-', 225 | ]; 226 | 227 | const COMPAREOP_MAP = [ 228 | OP\BinaryOp\EQ::class => '===', 229 | OP\BinaryOp\GE::class => '>=', 230 | OP\BinaryOp\GT::class => '>', 231 | OP\BinaryOp\LE::class => '<=', 232 | OP\BinaryOp\LT::class => '<', 233 | OP\BinaryOp\NE::class => '!==', 234 | ]; 235 | 236 | protected function compileBinaryOp(Op\BinaryOp $op): string { 237 | $class = get_class($op); 238 | if (isset(self::BINARYOP_MAP[$class])) { 239 | return " " . $this->temp($op->result) . " = {$this->valueMap[$op->left]} " . self::BINARYOP_MAP[$class] . " {$this->valueMap[$op->right]};\n"; 240 | } elseif (isset(self::COMPAREOP_MAP[$class])) { 241 | return " " . $this->temp($op->result) . " = {$this->valueMap[$op->left]} " . self::COMPAREOP_MAP[$class] . " {$this->valueMap[$op->right]};\n"; 242 | } else { 243 | throw new \LogicException("Unknown BinaryOp encountered: $class"); 244 | } 245 | } 246 | 247 | protected function buildResult(): CompiledUnit { 248 | eval($this->code); 249 | return new PHP\CompiledUnit($this, $this->code, $this->declaredFunctions); 250 | } 251 | 252 | } -------------------------------------------------------------------------------- /examples/03-iterated-function-calls/libjit.bc: -------------------------------------------------------------------------------- 1 | function add(l1 : long, l2 : long) : long 2 | incoming_reg(l1, rdi) 3 | incoming_reg(l2, rsi) 4 | .L: 5 | .L0: 6 | l5 = l1 + l2 7 | return_long(l5) 8 | ends_in_dead 9 | .L: 10 | .L: 11 | end 12 | 13 | function add100(l285 : long, l286 : long) : long 14 | incoming_reg(l285, rdi) 15 | incoming_reg(l286, rsi) 16 | .L: 17 | .L0: 18 | outgoing_reg(l286, rsi) 19 | outgoing_reg(l285, rdi) 20 | call add 21 | .L: 22 | return_reg(l291, rax) 23 | outgoing_reg(l286, rsi) 24 | outgoing_reg(l291, rdi) 25 | call add 26 | .L: 27 | return_reg(l295, rax) 28 | outgoing_reg(l286, rsi) 29 | outgoing_reg(l295, rdi) 30 | call add 31 | .L: 32 | return_reg(l298, rax) 33 | outgoing_reg(l286, rsi) 34 | outgoing_reg(l298, rdi) 35 | call add 36 | .L: 37 | return_reg(l301, rax) 38 | outgoing_reg(l286, rsi) 39 | outgoing_reg(l301, rdi) 40 | call add 41 | .L: 42 | return_reg(l304, rax) 43 | outgoing_reg(l286, rsi) 44 | outgoing_reg(l304, rdi) 45 | call add 46 | .L: 47 | return_reg(l307, rax) 48 | outgoing_reg(l286, rsi) 49 | outgoing_reg(l307, rdi) 50 | call add 51 | .L: 52 | return_reg(l310, rax) 53 | outgoing_reg(l286, rsi) 54 | outgoing_reg(l310, rdi) 55 | call add 56 | .L: 57 | return_reg(l313, rax) 58 | outgoing_reg(l286, rsi) 59 | outgoing_reg(l313, rdi) 60 | call add 61 | .L: 62 | return_reg(l316, rax) 63 | outgoing_reg(l286, rsi) 64 | outgoing_reg(l316, rdi) 65 | call add 66 | .L: 67 | return_reg(l319, rax) 68 | outgoing_reg(l286, rsi) 69 | outgoing_reg(l319, rdi) 70 | call add 71 | .L: 72 | return_reg(l322, rax) 73 | outgoing_reg(l286, rsi) 74 | outgoing_reg(l322, rdi) 75 | call add 76 | .L: 77 | return_reg(l325, rax) 78 | outgoing_reg(l286, rsi) 79 | outgoing_reg(l325, rdi) 80 | call add 81 | .L: 82 | return_reg(l328, rax) 83 | outgoing_reg(l286, rsi) 84 | outgoing_reg(l328, rdi) 85 | call add 86 | .L: 87 | return_reg(l331, rax) 88 | outgoing_reg(l286, rsi) 89 | outgoing_reg(l331, rdi) 90 | call add 91 | .L: 92 | return_reg(l334, rax) 93 | outgoing_reg(l286, rsi) 94 | outgoing_reg(l334, rdi) 95 | call add 96 | .L: 97 | return_reg(l337, rax) 98 | outgoing_reg(l286, rsi) 99 | outgoing_reg(l337, rdi) 100 | call add 101 | .L: 102 | return_reg(l340, rax) 103 | outgoing_reg(l286, rsi) 104 | outgoing_reg(l340, rdi) 105 | call add 106 | .L: 107 | return_reg(l343, rax) 108 | outgoing_reg(l286, rsi) 109 | outgoing_reg(l343, rdi) 110 | call add 111 | .L: 112 | return_reg(l346, rax) 113 | outgoing_reg(l286, rsi) 114 | outgoing_reg(l346, rdi) 115 | call add 116 | .L: 117 | return_reg(l349, rax) 118 | outgoing_reg(l286, rsi) 119 | outgoing_reg(l349, rdi) 120 | call add 121 | .L: 122 | return_reg(l352, rax) 123 | outgoing_reg(l286, rsi) 124 | outgoing_reg(l352, rdi) 125 | call add 126 | .L: 127 | return_reg(l355, rax) 128 | outgoing_reg(l286, rsi) 129 | outgoing_reg(l355, rdi) 130 | call add 131 | .L: 132 | return_reg(l216, rax) 133 | outgoing_reg(l286, rsi) 134 | outgoing_reg(l216, rdi) 135 | call add 136 | .L: 137 | return_reg(l219, rax) 138 | outgoing_reg(l286, rsi) 139 | outgoing_reg(l219, rdi) 140 | call add 141 | .L: 142 | return_reg(l222, rax) 143 | outgoing_reg(l286, rsi) 144 | outgoing_reg(l222, rdi) 145 | call add 146 | .L: 147 | return_reg(l225, rax) 148 | outgoing_reg(l286, rsi) 149 | outgoing_reg(l225, rdi) 150 | call add 151 | .L: 152 | return_reg(l228, rax) 153 | outgoing_reg(l286, rsi) 154 | outgoing_reg(l228, rdi) 155 | call add 156 | .L: 157 | return_reg(l231, rax) 158 | outgoing_reg(l286, rsi) 159 | outgoing_reg(l231, rdi) 160 | call add 161 | .L: 162 | return_reg(l234, rax) 163 | outgoing_reg(l286, rsi) 164 | outgoing_reg(l234, rdi) 165 | call add 166 | .L: 167 | return_reg(l237, rax) 168 | outgoing_reg(l286, rsi) 169 | outgoing_reg(l237, rdi) 170 | call add 171 | .L: 172 | return_reg(l240, rax) 173 | outgoing_reg(l286, rsi) 174 | outgoing_reg(l240, rdi) 175 | call add 176 | .L: 177 | return_reg(l243, rax) 178 | outgoing_reg(l286, rsi) 179 | outgoing_reg(l243, rdi) 180 | call add 181 | .L: 182 | return_reg(l246, rax) 183 | outgoing_reg(l286, rsi) 184 | outgoing_reg(l246, rdi) 185 | call add 186 | .L: 187 | return_reg(l249, rax) 188 | outgoing_reg(l286, rsi) 189 | outgoing_reg(l249, rdi) 190 | call add 191 | .L: 192 | return_reg(l252, rax) 193 | outgoing_reg(l286, rsi) 194 | outgoing_reg(l252, rdi) 195 | call add 196 | .L: 197 | return_reg(l255, rax) 198 | outgoing_reg(l286, rsi) 199 | outgoing_reg(l255, rdi) 200 | call add 201 | .L: 202 | return_reg(l258, rax) 203 | outgoing_reg(l286, rsi) 204 | outgoing_reg(l258, rdi) 205 | call add 206 | .L: 207 | return_reg(l261, rax) 208 | outgoing_reg(l286, rsi) 209 | outgoing_reg(l261, rdi) 210 | call add 211 | .L: 212 | return_reg(l264, rax) 213 | outgoing_reg(l286, rsi) 214 | outgoing_reg(l264, rdi) 215 | call add 216 | .L: 217 | return_reg(l267, rax) 218 | outgoing_reg(l286, rsi) 219 | outgoing_reg(l267, rdi) 220 | call add 221 | .L: 222 | return_reg(l270, rax) 223 | outgoing_reg(l286, rsi) 224 | outgoing_reg(l270, rdi) 225 | call add 226 | .L: 227 | return_reg(l273, rax) 228 | outgoing_reg(l286, rsi) 229 | outgoing_reg(l273, rdi) 230 | call add 231 | .L: 232 | return_reg(l276, rax) 233 | outgoing_reg(l286, rsi) 234 | outgoing_reg(l276, rdi) 235 | call add 236 | .L: 237 | return_reg(l279, rax) 238 | outgoing_reg(l286, rsi) 239 | outgoing_reg(l279, rdi) 240 | call add 241 | .L: 242 | return_reg(l282, rax) 243 | outgoing_reg(l286, rsi) 244 | outgoing_reg(l282, rdi) 245 | call add 246 | .L: 247 | return_reg(l143, rax) 248 | outgoing_reg(l286, rsi) 249 | outgoing_reg(l143, rdi) 250 | call add 251 | .L: 252 | return_reg(l146, rax) 253 | outgoing_reg(l286, rsi) 254 | outgoing_reg(l146, rdi) 255 | call add 256 | .L: 257 | return_reg(l149, rax) 258 | outgoing_reg(l286, rsi) 259 | outgoing_reg(l149, rdi) 260 | call add 261 | .L: 262 | return_reg(l152, rax) 263 | outgoing_reg(l286, rsi) 264 | outgoing_reg(l152, rdi) 265 | call add 266 | .L: 267 | return_reg(l155, rax) 268 | outgoing_reg(l286, rsi) 269 | outgoing_reg(l155, rdi) 270 | call add 271 | .L: 272 | return_reg(l158, rax) 273 | outgoing_reg(l286, rsi) 274 | outgoing_reg(l158, rdi) 275 | call add 276 | .L: 277 | return_reg(l161, rax) 278 | outgoing_reg(l286, rsi) 279 | outgoing_reg(l161, rdi) 280 | call add 281 | .L: 282 | return_reg(l164, rax) 283 | outgoing_reg(l286, rsi) 284 | outgoing_reg(l164, rdi) 285 | call add 286 | .L: 287 | return_reg(l167, rax) 288 | outgoing_reg(l286, rsi) 289 | outgoing_reg(l167, rdi) 290 | call add 291 | .L: 292 | return_reg(l170, rax) 293 | outgoing_reg(l286, rsi) 294 | outgoing_reg(l170, rdi) 295 | call add 296 | .L: 297 | return_reg(l173, rax) 298 | outgoing_reg(l286, rsi) 299 | outgoing_reg(l173, rdi) 300 | call add 301 | .L: 302 | return_reg(l176, rax) 303 | outgoing_reg(l286, rsi) 304 | outgoing_reg(l176, rdi) 305 | call add 306 | .L: 307 | return_reg(l179, rax) 308 | outgoing_reg(l286, rsi) 309 | outgoing_reg(l179, rdi) 310 | call add 311 | .L: 312 | return_reg(l182, rax) 313 | outgoing_reg(l286, rsi) 314 | outgoing_reg(l182, rdi) 315 | call add 316 | .L: 317 | return_reg(l185, rax) 318 | outgoing_reg(l286, rsi) 319 | outgoing_reg(l185, rdi) 320 | call add 321 | .L: 322 | return_reg(l188, rax) 323 | outgoing_reg(l286, rsi) 324 | outgoing_reg(l188, rdi) 325 | call add 326 | .L: 327 | return_reg(l191, rax) 328 | outgoing_reg(l286, rsi) 329 | outgoing_reg(l191, rdi) 330 | call add 331 | .L: 332 | return_reg(l194, rax) 333 | outgoing_reg(l286, rsi) 334 | outgoing_reg(l194, rdi) 335 | call add 336 | .L: 337 | return_reg(l197, rax) 338 | outgoing_reg(l286, rsi) 339 | outgoing_reg(l197, rdi) 340 | call add 341 | .L: 342 | return_reg(l200, rax) 343 | outgoing_reg(l286, rsi) 344 | outgoing_reg(l200, rdi) 345 | call add 346 | .L: 347 | return_reg(l203, rax) 348 | outgoing_reg(l286, rsi) 349 | outgoing_reg(l203, rdi) 350 | call add 351 | .L: 352 | return_reg(l206, rax) 353 | outgoing_reg(l286, rsi) 354 | outgoing_reg(l206, rdi) 355 | call add 356 | .L: 357 | return_reg(l209, rax) 358 | outgoing_reg(l286, rsi) 359 | outgoing_reg(l209, rdi) 360 | call add 361 | .L: 362 | return_reg(l212, rax) 363 | outgoing_reg(l286, rsi) 364 | outgoing_reg(l212, rdi) 365 | call add 366 | .L: 367 | return_reg(l73, rax) 368 | outgoing_reg(l286, rsi) 369 | outgoing_reg(l73, rdi) 370 | call add 371 | .L: 372 | return_reg(l76, rax) 373 | outgoing_reg(l286, rsi) 374 | outgoing_reg(l76, rdi) 375 | call add 376 | .L: 377 | return_reg(l79, rax) 378 | outgoing_reg(l286, rsi) 379 | outgoing_reg(l79, rdi) 380 | call add 381 | .L: 382 | return_reg(l82, rax) 383 | outgoing_reg(l286, rsi) 384 | outgoing_reg(l82, rdi) 385 | call add 386 | .L: 387 | return_reg(l85, rax) 388 | outgoing_reg(l286, rsi) 389 | outgoing_reg(l85, rdi) 390 | call add 391 | .L: 392 | return_reg(l88, rax) 393 | outgoing_reg(l286, rsi) 394 | outgoing_reg(l88, rdi) 395 | call add 396 | .L: 397 | return_reg(l91, rax) 398 | outgoing_reg(l286, rsi) 399 | outgoing_reg(l91, rdi) 400 | call add 401 | .L: 402 | return_reg(l94, rax) 403 | outgoing_reg(l286, rsi) 404 | outgoing_reg(l94, rdi) 405 | call add 406 | .L: 407 | return_reg(l97, rax) 408 | outgoing_reg(l286, rsi) 409 | outgoing_reg(l97, rdi) 410 | call add 411 | .L: 412 | return_reg(l100, rax) 413 | outgoing_reg(l286, rsi) 414 | outgoing_reg(l100, rdi) 415 | call add 416 | .L: 417 | return_reg(l103, rax) 418 | outgoing_reg(l286, rsi) 419 | outgoing_reg(l103, rdi) 420 | call add 421 | .L: 422 | return_reg(l106, rax) 423 | outgoing_reg(l286, rsi) 424 | outgoing_reg(l106, rdi) 425 | call add 426 | .L: 427 | return_reg(l109, rax) 428 | outgoing_reg(l286, rsi) 429 | outgoing_reg(l109, rdi) 430 | call add 431 | .L: 432 | return_reg(l112, rax) 433 | outgoing_reg(l286, rsi) 434 | outgoing_reg(l112, rdi) 435 | call add 436 | .L: 437 | return_reg(l115, rax) 438 | outgoing_reg(l286, rsi) 439 | outgoing_reg(l115, rdi) 440 | call add 441 | .L: 442 | return_reg(l118, rax) 443 | outgoing_reg(l286, rsi) 444 | outgoing_reg(l118, rdi) 445 | call add 446 | .L: 447 | return_reg(l121, rax) 448 | outgoing_reg(l286, rsi) 449 | outgoing_reg(l121, rdi) 450 | call add 451 | .L: 452 | return_reg(l124, rax) 453 | outgoing_reg(l286, rsi) 454 | outgoing_reg(l124, rdi) 455 | call add 456 | .L: 457 | return_reg(l127, rax) 458 | outgoing_reg(l286, rsi) 459 | outgoing_reg(l127, rdi) 460 | call add 461 | .L: 462 | return_reg(l130, rax) 463 | outgoing_reg(l286, rsi) 464 | outgoing_reg(l130, rdi) 465 | call add 466 | .L: 467 | return_reg(l133, rax) 468 | outgoing_reg(l286, rsi) 469 | outgoing_reg(l133, rdi) 470 | call add 471 | .L: 472 | return_reg(l136, rax) 473 | outgoing_reg(l286, rsi) 474 | outgoing_reg(l136, rdi) 475 | call add 476 | .L: 477 | return_reg(l139, rax) 478 | outgoing_reg(l286, rsi) 479 | outgoing_reg(l139, rdi) 480 | call add 481 | .L: 482 | return_reg(l142, rax) 483 | outgoing_reg(l286, rsi) 484 | outgoing_reg(l142, rdi) 485 | call add 486 | .L: 487 | return_reg(l3, rax) 488 | outgoing_reg(l286, rsi) 489 | outgoing_reg(l3, rdi) 490 | call add 491 | .L: 492 | return_reg(l6, rax) 493 | outgoing_reg(l286, rsi) 494 | outgoing_reg(l6, rdi) 495 | call add 496 | .L: 497 | return_reg(l9, rax) 498 | outgoing_reg(l286, rsi) 499 | outgoing_reg(l9, rdi) 500 | call add 501 | .L: 502 | return_reg(l12, rax) 503 | outgoing_reg(l286, rsi) 504 | outgoing_reg(l12, rdi) 505 | call add 506 | .L: 507 | return_reg(l15, rax) 508 | outgoing_reg(l286, rsi) 509 | outgoing_reg(l15, rdi) 510 | call add 511 | .L: 512 | return_reg(l18, rax) 513 | outgoing_reg(l286, rsi) 514 | outgoing_reg(l18, rdi) 515 | call add 516 | .L: 517 | return_reg(l21, rax) 518 | return_long(l21) 519 | ends_in_dead 520 | .L: 521 | .L: 522 | end 523 | 524 | -------------------------------------------------------------------------------- /lib/Builder/BlockBuilder.php: -------------------------------------------------------------------------------- 1 | block = $block; 22 | $this->arguments = new SplObjectStorage; 23 | } 24 | 25 | public function add(Value $left, Value $right): Value { 26 | $result = $this->computeBinaryNumericResult($left, $right); 27 | $this->block->addOp(new Op\BinaryOp\Add($this->hoistToArg($left), $this->hoistToArg($right), $result)); 28 | return $result; 29 | } 30 | 31 | public function bitwiseAnd(Value $left, Value $right): Value { 32 | $result = $this->computeBinaryNumericResult($left, $right); 33 | $this->block->addOp(new Op\BinaryOp\BitwiseAnd($this->hoistToArg($left), $this->hoistToArg($right), $result)); 34 | return $result; 35 | } 36 | 37 | public function bitwiseOr(Value $left, Value $right): Value { 38 | $result = $this->computeBinaryNumericResult($left, $right); 39 | $this->block->addOp(new Op\BinaryOp\BitwiseOr($this->hoistToArg($left), $this->hoistToArg($right), $result)); 40 | return $result; 41 | } 42 | 43 | public function BitwiseXor(Value $left, Value $right): Value { 44 | $result = $this->computeBinaryNumericResult($left, $right); 45 | $this->block->addOp(new Op\BinaryOp\BitwiseXor($this->hoistToArg($left), $this->hoistToArg($right), $result)); 46 | return $result; 47 | } 48 | 49 | public function div(Value $left, Value $right): Value { 50 | $result = $this->computeBinaryNumericResult($left, $right); 51 | $this->block->addOp(new Op\BinaryOp\Div($this->hoistToArg($left), $this->hoistToArg($right), $result)); 52 | return $result; 53 | } 54 | 55 | public function eq(Value $left, Value $right): Value { 56 | $result = $this->computeBinaryLogicalResult($left, $right); 57 | $this->block->addOp(new Op\BinaryOp\EQ($this->hoistToArg($left), $this->hoistToArg($right), $result)); 58 | return $result; 59 | } 60 | 61 | public function ge(Value $left, Value $right): Value { 62 | $result = $this->computeBinaryLogicalResult($left, $right); 63 | $this->block->addOp(new Op\BinaryOp\GE($this->hoistToArg($left), $this->hoistToArg($right), $result)); 64 | return $result; 65 | } 66 | 67 | public function gt(Value $left, Value $right): Value { 68 | $result = $this->computeBinaryLogicalResult($left, $right); 69 | $this->block->addOp(new Op\BinaryOp\GT($this->hoistToArg($left), $this->hoistToArg($right), $result)); 70 | return $result; 71 | } 72 | 73 | public function le(Value $left, Value $right): Value { 74 | $result = $this->computeBinaryLogicalResult($left, $right); 75 | $this->block->addOp(new Op\BinaryOp\LE($this->hoistToArg($left), $this->hoistToArg($right), $result)); 76 | return $result; 77 | } 78 | 79 | public function logicalAnd(Value $left, Value $right): Value { 80 | $result = $this->computeBinaryLogicalResult($left, $right); 81 | $this->block->addOp(new Op\BinaryOp\LogicalAnd($this->hoistToArg($left), $this->hoistToArg($right), $result)); 82 | return $result; 83 | } 84 | 85 | public function logicalOr(Value $left, Value $right): Value { 86 | $result = $this->computeBinaryLogicalResult($left, $right); 87 | $this->block->addOp(new Op\BinaryOp\LogicalOr($this->hoistToArg($left), $this->hoistToArg($right), $result)); 88 | return $result; 89 | } 90 | 91 | public function lt(Value $left, Value $right): Value { 92 | $result = $this->computeBinaryLogicalResult($left, $right); 93 | $this->block->addOp(new Op\BinaryOp\LT($this->hoistToArg($left), $this->hoistToArg($right), $result)); 94 | return $result; 95 | } 96 | 97 | public function mod(Value $left, Value $right): Value { 98 | $result = $this->computeBinaryNumericResult($left, $right); 99 | $this->block->addOp(new Op\BinaryOp\Mod($this->hoistToArg($left), $this->hoistToArg($right), $result)); 100 | return $result; 101 | } 102 | 103 | public function mul(Value $left, Value $right): Value { 104 | $result = $this->computeBinaryNumericResult($left, $right); 105 | $this->block->addOp(new Op\BinaryOp\Mul($this->hoistToArg($left), $this->hoistToArg($right), $result)); 106 | return $result; 107 | } 108 | 109 | public function ne(Value $left, Value $right): Value { 110 | $result = $this->computeBinaryLogicalResult($left, $right); 111 | $this->block->addOp(new Op\BinaryOp\NE($this->hoistToArg($left), $this->hoistToArg($right), $result)); 112 | return $result; 113 | } 114 | 115 | public function sl(Value $left, Value $right): Value { 116 | $result = $this->computeBinaryNumericResult($left, $right); 117 | $this->block->addOp(new Op\BinaryOp\SL($this->hoistToArg($left), $this->hoistToArg($right), $result)); 118 | return $result; 119 | } 120 | 121 | public function sr(Value $left, Value $right): Value { 122 | $result = $this->computeBinaryNumericResult($left, $right); 123 | $this->block->addOp(new Op\BinaryOp\SR($this->hoistToArg($left), $this->hoistToArg($right), $result)); 124 | return $result; 125 | } 126 | 127 | public function sub(Value $left, Value $right): Value { 128 | $result = $this->computeBinaryNumericResult($left, $right); 129 | $this->block->addOp(new Op\BinaryOp\Sub($this->hoistToArg($left), $this->hoistToArg($right), $result)); 130 | return $result; 131 | } 132 | 133 | public function minus(Value $value): Value { 134 | $result = $this->computeUnaryNumericResult($value); 135 | $this->block->addOp(new Op\UnaryOp\Minus($this->hoistToArg($value), $result)); 136 | return $result; 137 | } 138 | 139 | public function bitwiseNot(Value $value): Value { 140 | $result = $this->computeUnaryNumericResult($value); 141 | $this->block->addOp(new Op\UnaryOp\BitwiseNot($this->hoistToArg($value), $result)); 142 | return $result; 143 | } 144 | 145 | public function LogicalNot(Value $value): Value { 146 | $result = $this->computeUnaryLogicalResult($value); 147 | $this->block->addOp(new Op\UnaryOp\LogicalNot($this->hoistToArg($value), $result)); 148 | return $result; 149 | } 150 | 151 | public function cast(Value $value, Type $type): Value { 152 | // todo: type check the cast to ensure it's from/to correct pointers 153 | $result = new Value\Value($this->block, $type); 154 | $this->block->addOp(new Op\UnaryOp\Cast($this->hoistToArg($value), $result)); 155 | return $result; 156 | } 157 | 158 | public function call(FunctionBuilder $function, Value ... $args): Value { 159 | $result = new Value\Value($this->block, $function->function->returnType); 160 | $this->block->addOp(new Op\Call($function->function, $result, ...$args)); 161 | return $result; 162 | } 163 | 164 | public function callNoReturn(FunctionBuilder $function, Value ... $args): void { 165 | $this->block->addOp(new Op\CallNoReturn($function->function, ...$args)); 166 | } 167 | 168 | public function malloc(Type $type): Value { 169 | $result = new Value\Value($this->block, $type); 170 | $this->block->addOp(new Op\Malloc($result)); 171 | return $result; 172 | } 173 | 174 | public function realloc(Value $value, Type $type): Value { 175 | // Todo: typecheck value->type and new->type are compatible pointers 176 | $result = new Value\Value($this->block, $newType); 177 | $this->block->addOp(new Op\Realloc($this->hoistToArg($value), $result)); 178 | return $result; 179 | } 180 | 181 | public function free(Value $value): void { 182 | $this->block->addOp(new Op\Free($this->hoistToArg($value))); 183 | } 184 | 185 | public function readField(Value $struct, string $name): Value { 186 | if (!$struct->type instanceof Type\Struct) { 187 | throw new \LogicException("Attempting to read a field from a non-struct"); 188 | } 189 | $field = $struct->type->field($name); 190 | $result = new Value\Value($this->block, $field->type); 191 | $this->block->addOp(new Op\FieldRead($this->hoistToArg($struct), $field, $result)); 192 | return $result; 193 | } 194 | 195 | public function writeField(Value $struct, string $name, Value $value): void { 196 | if (!$struct->type instanceof Type\Struct) { 197 | throw new \LogicException("Attempting to write a field from a non-struct"); 198 | } 199 | $field = $struct->type->field($name); 200 | if ($field->type !== $value->type) { 201 | throw new \LogicException("Attempting to write a field of different types"); 202 | } 203 | $this->block->addOp(new Op\FieldWrite($this->hoistToArg($struct), $field, $value)); 204 | } 205 | 206 | public function jump(BlockBuilder $block): void { 207 | $this->block->addOp(new Op\BlockCall($block->block)); 208 | } 209 | 210 | public function jumpIf(Value $cond, BlockBuilder $ifTrue, BlockBuilder $ifFalse): void { 211 | $this->block->addOp(new Op\ConditionalBlockCall($cond, $ifTrue->block, $ifFalse->block)); 212 | } 213 | 214 | public function returnVoid(): void { 215 | $this->block->addOp(new Op\ReturnVoid()); 216 | } 217 | 218 | public function returnValue(Value $value): void { 219 | $this->block->addOp(new Op\ReturnValue($this->hoistToArg($value))); 220 | } 221 | 222 | public function hoistToArg(Value $value): Value { 223 | if (!$value->isOwnedBy($this->block)) { 224 | if ($this->arguments->contains($value)) { 225 | return $this->arguments[$value]; 226 | } 227 | $newValue = new Value\Value($this->block, $value->type); 228 | $this->arguments[$value] = $newValue; 229 | return $newValue; 230 | } 231 | return $value; 232 | } 233 | 234 | private function computeBinaryNumericResult(Value $left, Value $right): Value { 235 | if ($left->type === $right->type) { 236 | $type = $left->type; 237 | } else { 238 | var_dump($left->type, $right->type); 239 | throw new \LogicException("BinaryOps between different types are not supported yet"); 240 | } 241 | return new Value\Value($this->block, $type); 242 | } 243 | 244 | private function computeBinaryLogicalResult(Value $left, Value $right): Value { 245 | return new Value\Value($this->block, $this->type()->bool()); 246 | } 247 | 248 | private function computeUnaryNumericResult(Value $value): Value { 249 | // all types are numeric types, so :D 250 | return new Value\Value($this->block, $value->type); 251 | } 252 | 253 | private function computeUnaryLogicalResult(Value $value): Value { 254 | return new Value\Value($this->block, $this->type()->bool()); 255 | } 256 | 257 | public function finish(): void { 258 | if (!$this->block->isClosed) { 259 | if ($this->parent->function->returnType->isVoid()) { 260 | $this->returnVoid(); 261 | } else { 262 | throw new \LogicException("Attempting to finish a non-void function with an unclosed block, you must terminate"); 263 | } 264 | } 265 | } 266 | 267 | public function getTargetBlocks(): array { 268 | foreach ($this->block->ops as $op) { 269 | if ($op instanceof TerminalOp) { 270 | return $op->getTargetBlocks(); 271 | } 272 | } 273 | throw new \LogicException("Block does not contain any TerminalOp, was finish() called?"); 274 | } 275 | } -------------------------------------------------------------------------------- /lib/Backend/LIBGCCJIT.php: -------------------------------------------------------------------------------- 1 | lib::GCC_JIT_TYPE_VOID, 38 | Type\Primitive::T_VOID_PTR => lib::GCC_JIT_TYPE_VOID_PTR, 39 | Type\Primitive::T_BOOL => lib::GCC_JIT_TYPE_BOOL, 40 | Type\Primitive::T_CHAR => lib::GCC_JIT_TYPE_CHAR, 41 | Type\Primitive::T_SIGNED_CHAR => lib::GCC_JIT_TYPE_SIGNED_CHAR, 42 | Type\Primitive::T_UNSIGNED_CHAR => lib::GCC_JIT_TYPE_UNSIGNED_CHAR, 43 | Type\Primitive::T_SHORT => lib::GCC_JIT_TYPE_SHORT, 44 | Type\Primitive::T_UNSIGNED_SHORT => lib::GCC_JIT_TYPE_UNSIGNED_SHORT, 45 | Type\Primitive::T_INT => lib::GCC_JIT_TYPE_INT, 46 | Type\Primitive::T_UNSIGNED_INT => lib::GCC_JIT_TYPE_UNSIGNED_INT, 47 | Type\Primitive::T_LONG => lib::GCC_JIT_TYPE_LONG, 48 | Type\Primitive::T_UNSIGNED_LONG => lib::GCC_JIT_TYPE_UNSIGNED_LONG, 49 | Type\Primitive::T_LONG_LONG => lib::GCC_JIT_TYPE_LONG_LONG, 50 | Type\Primitive::T_UNSIGNED_LONG_LONG => lib::GCC_JIT_TYPE_UNSIGNED_LONG_LONG, 51 | Type\Primitive::T_FLOAT => lib::GCC_JIT_TYPE_FLOAT, 52 | Type\Primitive::T_DOUBLE => lib::GCC_JIT_TYPE_DOUBLE, 53 | Type\Primitive::T_LONG_DOUBLE => lib::GCC_JIT_TYPE_LONG_DOUBLE, 54 | Type\Primitive::T_SIZE_T => lib::GCC_JIT_TYPE_SIZE_T, 55 | ]; 56 | 57 | public function __construct() { 58 | $this->lib = new lib; 59 | } 60 | 61 | protected function beforeCompile(Context $context, int $optimizationLevel): void { 62 | $this->parameterMap = []; 63 | $this->fieldMap = new SplObjectStorage; 64 | $this->structMap = new SplObjectStorage; 65 | $this->context = $this->lib->gcc_jit_context_acquire(); 66 | $this->lib->gcc_jit_context_set_int_option($this->context, lib::GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, $optimizationLevel); 67 | } 68 | 69 | protected function afterCompile(Context $context): void { 70 | unset($this->parameterMap); 71 | unset($this->context); 72 | unset($this->fieldMap); 73 | unset($this->structMap); 74 | } 75 | 76 | protected function compileType(Type $type) { 77 | if ($type instanceof Type\Primitive) { 78 | return $this->lib->gcc_jit_context_get_type($this->context, self::PRIMITIVE_TYPE_MAP[$type->kind]); 79 | } elseif ($type instanceof Type\Struct) { 80 | $fieldWrapper = $this->lib->makeArray( 81 | gcc_jit_field_ptr_ptr::class, 82 | array_map( 83 | function(Type\Struct\Field $field) { 84 | $fieldPtr = $this->lib->gcc_jit_context_new_field($this->context, null, $this->typeMap[$field->type], $field->name); 85 | $this->fieldMap[$field] = $fieldPtr; 86 | return $fieldPtr; 87 | }, 88 | $type->fields 89 | ) 90 | ); 91 | $struct = $this->lib->gcc_jit_context_new_struct_type($this->context, null, $type->name, count($type->fields), $fieldWrapper); 92 | $this->structMap[$type] = $struct; 93 | return $this->lib->gcc_jit_struct_as_type($struct); 94 | } 95 | throw new \LogicException("Not implemented type for gcc_jit: " . get_class($type)); 96 | } 97 | 98 | protected function compileConstant(Constant $constant) { 99 | // libjit allocates constants per function, do nothing 100 | if ($constant->type->isFloatingPoint()) { 101 | return $this->lib->gcc_jit_context_new_rvalue_from_double($this->context, $this->typeMap[$constant->type], $constant->value); 102 | } 103 | return $this->lib->gcc_jit_context_new_rvalue_from_long($this->context, $this->typeMap[$constant->type], $constant->value); 104 | } 105 | 106 | protected function importFunction(Function_ $function) { 107 | $params = $this->lib->makeArray( 108 | gcc_jit_param_ptr_ptr::class, 109 | array_map( 110 | function(Parameter $param) { 111 | return $this->lib->gcc_jit_context_new_param($this->context, null, $this->typeMap[$param->type], $param->name); 112 | }, 113 | $function->parameters 114 | ) 115 | ); 116 | $func = $this->lib->gcc_jit_context_new_function( 117 | $this->context, 118 | null, 119 | lib::GCC_JIT_FUNCTION_IMPORTED, 120 | $this->typeMap[$function->returnType], 121 | $function->name, 122 | count($function->parameters), 123 | $params, 124 | $function->isVariadic ? 1 : 0 125 | ); 126 | if ($func === null) { 127 | throw new \LogicException("Func shouldn't be null"); 128 | } 129 | return $func; 130 | } 131 | 132 | protected array $parameterMap; 133 | 134 | protected function declareFunction(Function_ $function) { 135 | $this->parameterMap[$function->name] = array_map( 136 | function(Parameter $param) { 137 | return $this->lib->gcc_jit_context_new_param($this->context, null, $this->typeMap[$param->type], $param->name); 138 | }, 139 | $function->parameters 140 | ); 141 | $params = $this->lib->makeArray( 142 | gcc_jit_param_ptr_ptr::class, 143 | $this->parameterMap[$function->name] 144 | ); 145 | if ($function instanceof Function_\AlwaysInline) { 146 | $type = lib::GCC_JIT_FUNCTION_ALWAYS_INLINE; 147 | } elseif ($function instanceof Function_\Static_) { 148 | $type = lib::GCC_JIT_FUNCTION_INTERNAL; 149 | } else { 150 | $type = lib::GCC_JIT_FUNCTION_EXPORTED; 151 | } 152 | $func = $this->lib->gcc_jit_context_new_function( 153 | $this->context, 154 | null, 155 | $type, 156 | $this->typeMap[$function->returnType], 157 | $function->name, 158 | count($function->parameters), 159 | $params, 160 | $function->isVariadic ? 1 : 0 161 | ); 162 | if ($func === null) { 163 | throw new \LogicException("Func shouldn't be null"); 164 | } 165 | return $func; 166 | } 167 | 168 | private SplObjectStorage $localMap; 169 | private SplObjectStorage $valueMap; 170 | private SplObjectStorage $blockMap; 171 | 172 | protected function compileFunction(Function_ $function, $func): void { 173 | $this->valueMap = new SplObjectStorage; 174 | $this->blockMap = new SplObjectStorage; 175 | $this->localMap = new SplObjectStorage; 176 | foreach ($this->constantMap as $constant) { 177 | $this->valueMap[$constant] = $this->constantMap[$constant]; 178 | } 179 | foreach ($function->parameters as $index => $parameter) { 180 | $this->valueMap[$parameter->value] = $this->lib->gcc_jit_param_as_rvalue($this->parameterMap[$function->name][$index]); 181 | } 182 | foreach ($function->locals as $local) { 183 | $this->localMap[$local] = $this->lib->gcc_jit_function_new_local($func, null, $this->typeMap[$local->type], $local->name); 184 | $this->valueMap[$local] = $this->lib->gcc_jit_lvalue_as_rvalue($this->localMap[$local]); 185 | } 186 | foreach ($function->blocks as $block) { 187 | $this->blockMap[$block] = $this->lib->gcc_jit_function_new_block($func, $block->name); 188 | foreach ($block->arguments as $idx => $argument) { 189 | $this->localMap[$argument] = $this->lib->gcc_jit_function_new_local($func, null, $this->typeMap[$argument->type], $block->name . '_arg_' . $idx); 190 | } 191 | } 192 | foreach ($function->blocks as $block) { 193 | $this->compileBlock($func, $block); 194 | } 195 | unset($this->localMap); 196 | unset($this->valueMap); 197 | unset($this->blockMap); 198 | } 199 | 200 | protected function compileBlock(gcc_jit_function_ptr $func, Block $block): void { 201 | foreach ($block->arguments as $argument) { 202 | $this->valueMap[$argument] = $this->lib->gcc_jit_lvalue_as_rvalue($this->localMap[$argument], null); 203 | } 204 | foreach ($block->ops as $op) { 205 | $this->compileOp($func, $this->blockMap[$block], $block, $op); 206 | } 207 | } 208 | 209 | protected function compileOp(gcc_jit_function_ptr $func, gcc_jit_block_ptr $block, Block $irBlock, Op $op): void { 210 | if ($op instanceof Op\BinaryOp) { 211 | $this->compileBinaryOp($op); 212 | } elseif ($op instanceof Op\ReturnVoid) { 213 | $this->lib->gcc_jit_block_end_with_void_return($block, null); 214 | } elseif ($op instanceof Op\ReturnValue) { 215 | $this->lib->gcc_jit_block_end_with_return($block, null, $this->valueMap[$op->value]); 216 | } elseif ($op instanceof Op\Call) { 217 | $this->valueMap[$op->return] = $this->doCall($func, $op); 218 | } elseif ($op instanceof Op\CallNoReturn) { 219 | $this->lib->gcc_jit_block_add_eval($block, null, $this->doCall($func, $op)); 220 | } elseif ($op instanceof Op\FieldRead) { 221 | $this->valueMap[$op->return] = $this->lib->gcc_jit_rvalue_access_field($this->valueMap[$op->struct], null, $this->fieldMap[$op->field]); 222 | } elseif ($op instanceof Op\FieldWrite) { 223 | if ($this->localMap->contains($op->struct)) { 224 | $this->lib->gcc_jit_block_add_assignment( 225 | $block, 226 | null, 227 | $this->lib->gcc_jit_lvalue_access_field($this->localMap[$op->struct], null, $this->fieldMap[$op->field]), 228 | $this->valueMap[$op->value] 229 | ); 230 | } else { 231 | throw new \LogicException("Cannot write to rvalue"); 232 | } 233 | } elseif ($op instanceof Op\BlockCall) { 234 | $this->compileBlockCall($block, $op); 235 | } elseif ($op instanceof Op\ConditionalBlockCall) { 236 | $if = $this->lib->gcc_jit_function_new_block($func, $irBlock->name . '_if'); 237 | $else = $this->lib->gcc_jit_function_new_block($func, $irBlock->name . '_else'); 238 | $this->lib->gcc_jit_block_end_with_conditional($block, null, $this->valueMap[$op->cond], $if, $else); 239 | $this->compileBlockCall($if, $op->ifTrue); 240 | $this->compileBlockCall($else, $op->ifFalse); 241 | } else { 242 | throw new \LogicException("Unknown Op encountered: " . get_class($op)); 243 | } 244 | } 245 | 246 | protected function doCall(gcc_jit_function_ptr $func, Op $op): gcc_jit_rvalue_ptr { 247 | $paramWrapper = $this->lib->makeArray( 248 | gcc_jit_rvalue_ptr_ptr::class, 249 | array_map( 250 | function(Value $value) { 251 | return $this->valueMap[$value]; 252 | }, 253 | $op->parameters 254 | ) 255 | ); 256 | return $this->lib->gcc_jit_context_new_call( 257 | $this->context, 258 | null, 259 | $this->functionMap[$op->function->name], 260 | count($op->parameters), 261 | $paramWrapper, 262 | ); 263 | } 264 | 265 | protected function compileBlockCall(gcc_jit_block_ptr $block, Op\BlockCall $op): void { 266 | foreach ($op->block->arguments as $index => $argument) { 267 | $this->lib->gcc_jit_block_add_assignment($block, null, $this->localMap[$argument], $this->valueMap[$op->arguments[$index]]); 268 | } 269 | $this->lib->gcc_jit_block_end_with_jump($block, null, $this->blockMap[$op->block]); 270 | } 271 | 272 | const BINARYOP_MAP = [ 273 | Op\BinaryOp\Add::class => lib::GCC_JIT_BINARY_OP_PLUS, 274 | Op\BinaryOp\BitwiseAnd::class => lib::GCC_JIT_BINARY_OP_BITWISE_AND, 275 | Op\BinaryOp\BitwiseOr::class => lib::GCC_JIT_BINARY_OP_BITWISE_OR, 276 | Op\BinaryOp\BitwiseXor::class => lib::GCC_JIT_BINARY_OP_BITWISE_XOR, 277 | Op\BinaryOp\Div::class => lib::GCC_JIT_BINARY_OP_DIVIDE, 278 | Op\BinaryOp\LogicalAnd::class => lib::GCC_JIT_BINARY_OP_LOGICAL_AND, 279 | Op\BinaryOp\LogicalOr::class => lib::GCC_JIT_BINARY_OP_LOGICAL_OR, 280 | Op\BinaryOp\Mod::class => lib::GCC_JIT_BINARY_OP_MODULO, 281 | Op\BinaryOp\Mul::class => lib::GCC_JIT_BINARY_OP_MULT, 282 | Op\BinaryOp\SL::class => lib::GCC_JIT_BINARY_OP_LSHIFT, 283 | Op\BinaryOp\SR::class => lib::GCC_JIT_BINARY_OP_RSHIFT, 284 | Op\BinaryOp\Sub::class => lib::GCC_JIT_BINARY_OP_MINUS, 285 | ]; 286 | 287 | const COMPAREOP_MAP = [ 288 | OP\BinaryOp\EQ::class => lib::GCC_JIT_COMPARISON_EQ, 289 | OP\BinaryOp\GE::class => lib::GCC_JIT_COMPARISON_GE, 290 | OP\BinaryOp\GT::class => lib::GCC_JIT_COMPARISON_GT, 291 | OP\BinaryOp\LE::class => lib::GCC_JIT_COMPARISON_LE, 292 | OP\BinaryOp\LT::class => lib::GCC_JIT_COMPARISON_LT, 293 | OP\BinaryOp\NE::class => lib::GCC_JIT_COMPARISON_NE, 294 | ]; 295 | 296 | protected function compileBinaryOp(Op\BinaryOp $op): void { 297 | $class = get_class($op); 298 | if (isset(self::BINARYOP_MAP[$class])) { 299 | $this->valueMap[$op->result] = $this->lib->gcc_jit_context_new_binary_op($this->context, null, self::BINARYOP_MAP[$class], $this->typeMap[$op->result->type], $this->valueMap[$op->left], $this->valueMap[$op->right]); 300 | } elseif (isset(self::COMPAREOP_MAP[$class])) { 301 | $this->valueMap[$op->result] = $this->lib->gcc_jit_context_new_comparison($this->context, null, self::COMPAREOP_MAP[$class], $this->valueMap[$op->left], $this->valueMap[$op->right]); 302 | } else { 303 | throw new \LogicException("Unknown BinaryOp encountered: $class"); 304 | } 305 | } 306 | 307 | protected function buildResult(): CompiledUnit { 308 | $result = $this->lib->gcc_jit_context_compile($this->context); 309 | return new LIBGCCJIT\CompiledUnit($this, $this->context, $result, $this->signatureMap); 310 | } 311 | 312 | } --------------------------------------------------------------------------------