diff --git a/ext/types/strlen.php b/ext/types/strlen.php index 3925e2e..12f80e6 100644 --- a/ext/types/strlen.php +++ b/ext/types/strlen.php @@ -1,7 +1,7 @@ orig = $block; - $this->scope = new \SplObjectStorage; - $this->args = new \SplObjectStorage; - } - - public function getOperand(int $offset): Operand { - foreach ($this->scope as $operand) { - if ($this->scope[$operand] === $offset) { - return $operand; - } - } - } - - public function getVarSlot(Operand $operand, bool $isRead): int { - if (!$this->scope->contains($operand)) { - $this->scope[$operand] = $this->scope->count(); - if ($isRead) { - $this->args[$operand] = $this->scope[$operand]; - } - } - return $this->scope[$operand]; - } - - public function registerConstant(Operand $operand, Variable $const): int { - $slot = $this->getVarSlot($operand, false); - $this->constants[$slot] = $const; - return $slot; - } - - public function addOpCode(OpCode ...$ops): void { - foreach ($ops as $op) { - $this->nOpCodes++; - $this->opCodes[] = $op; - } - } - - public function findSlot(Operand $op, Frame $frame): ?Variable { - if (!$this->scope->contains($op)) { - // check PHI vars - if (!is_null($frame->parent)) { - return $frame->parent->block->findSlot($op, $frame->parent); - } - return null; - } - $idx = $this->scope[$op]; - return $frame->scope[$idx]; - } - - public function getFrame(Context $context, ?Frame $frame = null): Frame { - // Todo: build scope - $scope = []; - $scopeSize = $this->scope->count(); - foreach ($this->scope as $op) { - $pos = $this->scope[$op]; - - if (isset($this->constants[$pos])) { - $scope[$pos] = $this->constants[$pos]; - } elseif ($this->args->contains($op)) { - if (is_null($frame)) { - throw new \LogicException("Argument var with no parent frame, illegal"); - } - $found = false; - $parent = $frame->block->findSlot($op, $frame); - if (!is_null($parent)) { - $scope[$pos] = $parent; - $found = true; - } - if (!$found) { - throw new \LogicException("Could not resolve argument"); - } - } else { - $scope[$pos] = new Variable(Variable::TYPE_NULL); - } - } - - $return = new Frame(null, $this, $frame, ...$scope); - if (!is_null($frame) && !is_null($frame->returnVar)) { - $return->returnVar = $frame->returnVar; - } - return $return; - } - - -} diff --git a/lib/JIT/Builtin.php b/lib/Builtin.php similarity index 96% rename from lib/JIT/Builtin.php rename to lib/Builtin.php index 5708183..79584fe 100755 --- a/lib/JIT/Builtin.php +++ b/lib/Builtin.php @@ -7,7 +7,7 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCompiler\JIT; +namespace PHPCompiler; abstract class Builtin { const LOAD_TYPE_EXPORT = 1; diff --git a/lib/JIT/Builtin/ErrorHandler.php b/lib/Builtin/ErrorHandler.php similarity index 100% rename from lib/JIT/Builtin/ErrorHandler.php rename to lib/Builtin/ErrorHandler.php diff --git a/lib/JIT/Builtin/Internal.php b/lib/Builtin/Internal.php similarity index 56% rename from lib/JIT/Builtin/Internal.php rename to lib/Builtin/Internal.php index 663a2a0..9937509 100644 --- a/lib/JIT/Builtin/Internal.php +++ b/lib/Builtin/Internal.php @@ -1,22 +1,22 @@ context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('size_t') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + '__mm__malloc', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + \PHPLLVM\Attribute::INDEX_FUNCTION, + $this->context->attributes['alwaysinline'] + ); + + $this->context->registerFunction('__mm__malloc', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('int8*'), + $this->context->getTypeFromString('size_t') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + '__mm__realloc', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + \PHPLLVM\Attribute::INDEX_FUNCTION, + $this->context->attributes['alwaysinline'] + ); + + $this->context->registerFunction('__mm__realloc', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('void'), + false, + $this->context->getTypeFromString('int8*') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + '__mm__free', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + \PHPLLVM\Attribute::INDEX_FUNCTION, + $this->context->attributes['alwaysinline'] + ); + + $this->context->registerFunction('__mm__free', $fn___cfcd208495d565ef66e7dff9f98764da); + } + + public function malloc(PHPLLVM\Type $type): PHPLLVM\Value + { + if ($type instanceof \PHPLLVM\Type) { + $type = $type; + } elseif ($type instanceof \PHPLLVM\Value) { + $type = $type->typeOf(); + } else { + throw new \LogicException("Attempt to call sizeof on non-PHPLLVM type/value"); + } + $size = $this->context->builder->ptrToInt( + $this->context->builder->gep( + $type->pointerType(0)->constNull(), + $this->context->context->int32Type()->constInt(1, false) + ), + $this->context->getTypeFromString('size_t') + ); + $ptr = $this->context->builder->call($this->context->lookupFunction('__mm__malloc'), $size); + + return $this->context->builder->pointerCast($ptr, $type->pointerType(0)); + } + + public function mallocWithExtra(PHPLLVM\Type $type, PHPLLVM\Value $extra): PHPLLVM\Value + { + if ($type instanceof \PHPLLVM\Type) { + $type = $type; + } elseif ($type instanceof \PHPLLVM\Value) { + $type = $type->typeOf(); + } else { + throw new \LogicException("Attempt to call sizeof on non-PHPLLVM type/value"); + } + $size = $this->context->builder->ptrToInt( + $this->context->builder->gep( + $type->pointerType(0)->constNull(), + $this->context->context->int32Type()->constInt(1, false) + ), + $this->context->getTypeFromString('size_t') + ); + $__right = $this->context->builder->intCast($extra, $size->typeOf()); + + $size = $this->context->builder->addNoUnsignedWrap($size, $__right); + $ptr = $this->context->builder->call($this->context->lookupFunction('__mm__malloc'), $size); + + return $this->context->builder->pointerCast($ptr, $type->pointerType(0)); + } + + public function realloc(PHPLLVM\Value $value, PHPLLVM\Value $extra): PHPLLVM\Value + { + $type = $value->typeOf()->getElementType(); + if ($type instanceof \PHPLLVM\Type) { + $type = $type; + } elseif ($type instanceof \PHPLLVM\Value) { + $type = $type->typeOf(); + } else { + throw new \LogicException("Attempt to call sizeof on non-PHPLLVM type/value"); + } + $size = $this->context->builder->ptrToInt( + $this->context->builder->gep( + $type->pointerType(0)->constNull(), + $this->context->context->int32Type()->constInt(1, false) + ), + $this->context->getTypeFromString('size_t') + ); + $__right = $this->context->builder->intCast($extra, $size->typeOf()); + + $allocSize = $this->context->builder->addNoUnsignedWrap($size, $__right); + $__type = $this->context->getTypeFromString('int8*'); + + $__kind = $__type->getKind(); + $__value = $value; + switch ($__kind) { + case \PHPLLVM\Type::KIND_INTEGER: + if (!is_object($__value)) { + $void = $__type->constInt($__value, false); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + if ($__other_type->getWidth() >= $__type->getWidth()) { + $void = $this->context->builder->truncOrBitCast($__value, $__type); + } else { + $void = $this->context->builder->zExtOrBitCast($__value, $__type); + } + break; + case \PHPLLVM\Type::KIND_DOUBLE: + $void = $this->context->builder->fpToUi($__value, $__type); + + break; + case \PHPLLVM\Type::KIND_ARRAY: + case \PHPLLVM\Type::KIND_POINTER: + $void = $this->context->builder->ptrToInt($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (int, " . $__other_type->toString() . ")" + ); + } + break; + case \PHPLLVM\Type::KIND_DOUBLE: + if (!is_object($__value)) { + $void = $__type->constReal($value); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + $void = $this->context->builder->uiToFp($__value, $__type); + + break; + case \PHPLLVM\Type::KIND_DOUBLE: + $void = $this->context->builder->fpCast($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (double, " . $__other_type->toString() . ")" + ); + } + break; + case \PHPLLVM\Type::KIND_ARRAY: + case \PHPLLVM\Type::KIND_POINTER: + if (!is_object($__value)) { + // this is very likely very wrong... + $void = $__type->constInt($__value, false); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + $void = $this->context->builder->intToPtr($__value, $__type); + break; + case \PHPLLVM\Type::KIND_ARRAY: + // $__tmp = $this->context->builder->($__value, $this->context->context->int64Type()); + // $(result) = $this->context->builder->intToPtr($__tmp, $__type); + // break; + case \PHPLLVM\Type::KIND_POINTER: + $void = $this->context->builder->pointerCast($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (double, " . $__other_type->toString() . ")" + ); + } + break; + default: + throw new \LogicException("Unsupported type cast: " . $__type->toString()); + } + $ptr = $this->context->builder->call($this->context->lookupFunction('__mm__realloc'), $void, $allocSize); + + return $this->context->builder->pointerCast($ptr, $type->pointerType(0)); + } + + public function free(PHPLLVM\Value $value): void + { + $__type = $this->context->getTypeFromString('int8*'); + + $__kind = $__type->getKind(); + $__value = $value; + switch ($__kind) { + case \PHPLLVM\Type::KIND_INTEGER: + if (!is_object($__value)) { + $void = $__type->constInt($__value, false); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + if ($__other_type->getWidth() >= $__type->getWidth()) { + $void = $this->context->builder->truncOrBitCast($__value, $__type); + } else { + $void = $this->context->builder->zExtOrBitCast($__value, $__type); + } + break; + case \PHPLLVM\Type::KIND_DOUBLE: + $void = $this->context->builder->fpToSi($__value, $__type); + + break; + case \PHPLLVM\Type::KIND_ARRAY: + case \PHPLLVM\Type::KIND_POINTER: + $void = $this->context->builder->ptrToInt($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (int, " . $__other_type->toString() . ")" + ); + } + break; + case \PHPLLVM\Type::KIND_DOUBLE: + if (!is_object($__value)) { + $void = $__type->constReal($value); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + $void = $this->context->builder->siToFp($__value, $__type); + + break; + case \PHPLLVM\Type::KIND_DOUBLE: + $void = $this->context->builder->fpCast($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (double, " . $__other_type->toString() . ")" + ); + } + break; + case \PHPLLVM\Type::KIND_ARRAY: + case \PHPLLVM\Type::KIND_POINTER: + if (!is_object($__value)) { + // this is very likely very wrong... + $void = $__type->constInt($__value, false); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + $void = $this->context->builder->intToPtr($__value, $__type); + break; + case \PHPLLVM\Type::KIND_ARRAY: + // $__tmp = $this->context->builder->($__value, $this->context->context->int64Type()); + // $(result) = $this->context->builder->intToPtr($__tmp, $__type); + // break; + case \PHPLLVM\Type::KIND_POINTER: + $void = $this->context->builder->pointerCast($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (double, " . $__other_type->toString() . ")" + ); + } + break; + default: + throw new \LogicException("Unsupported type cast: " . $__type->toString()); + } + $this->context->builder->call($this->context->lookupFunction('__mm__free'), $void); + } +} diff --git a/lib/JIT/Builtin/MemoryManager.pre b/lib/Builtin/MemoryManager.pre similarity index 100% rename from lib/JIT/Builtin/MemoryManager.pre rename to lib/Builtin/MemoryManager.pre diff --git a/lib/Builtin/MemoryManager/Native.php b/lib/Builtin/MemoryManager/Native.php new file mode 100644 index 0000000..e947e0f --- /dev/null +++ b/lib/Builtin/MemoryManager/Native.php @@ -0,0 +1,92 @@ +context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('size_t') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + 'malloc', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $this->context->registerFunction('malloc', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('int8*'), + $this->context->getTypeFromString('size_t') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + 'realloc', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $this->context->registerFunction('realloc', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('void'), + false, + $this->context->getTypeFromString('int8*') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + 'free', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $this->context->registerFunction('free', $fn___cfcd208495d565ef66e7dff9f98764da); + } + + public function implement(): void + { + // Todo + $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->lookupFunction('__mm__malloc'); + $block___c4ca4238a0b923820dcc509a6f75849b = $fn___c4ca4238a0b923820dcc509a6f75849b->appendBasicBlock('main'); + $this->context->builder->positionAtEnd($block___c4ca4238a0b923820dcc509a6f75849b); + $size = $fn___c4ca4238a0b923820dcc509a6f75849b->getParam(0); + + $result = $this->context->builder->call($this->context->lookupFunction('malloc'), $size); + $this->context->builder->returnValue($result); + + $this->context->builder->clearInsertionPosition(); + $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $this->context->lookupFunction('__mm__realloc'); + $block___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->appendBasicBlock('main'); + $this->context->builder->positionAtEnd($block___eccbc87e4b5ce2fe28308fd9f2a7baf3); + $void = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->getParam(0); + $size = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->getParam(1); + + $result = $this->context->builder->call($this->context->lookupFunction('realloc'), $void, $size); + $this->context->builder->returnValue($result); + + $this->context->builder->clearInsertionPosition(); + $fn___e4da3b7fbbce2345d7772b0674a318d5 = $this->context->lookupFunction('__mm__free'); + $block___e4da3b7fbbce2345d7772b0674a318d5 = $fn___e4da3b7fbbce2345d7772b0674a318d5->appendBasicBlock('main'); + $this->context->builder->positionAtEnd($block___e4da3b7fbbce2345d7772b0674a318d5); + $void = $fn___e4da3b7fbbce2345d7772b0674a318d5->getParam(0); + + $this->context->builder->call($this->context->lookupFunction('free'), $void); + $this->context->builder->returnVoid(); + + $this->context->builder->clearInsertionPosition(); + } +} diff --git a/lib/JIT/Builtin/MemoryManager/Native.pre b/lib/Builtin/MemoryManager/Native.pre similarity index 100% rename from lib/JIT/Builtin/MemoryManager/Native.pre rename to lib/Builtin/MemoryManager/Native.pre diff --git a/lib/Builtin/MemoryManager/PHP.php b/lib/Builtin/MemoryManager/PHP.php new file mode 100755 index 0000000..7ab6643 --- /dev/null +++ b/lib/Builtin/MemoryManager/PHP.php @@ -0,0 +1,353 @@ +context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('size_t'), + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('uint32'), + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('uint32') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + '_emalloc', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 1 + 1, + $this->context->attributes['readonly'], + 0 + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 1 + 1, + $this->context->attributes['nocapture'], + 0 + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 3 + 1, + $this->context->attributes['readonly'], + 0 + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 3 + 1, + $this->context->attributes['nocapture'], + 0 + ); + + $this->context->registerFunction('_emalloc', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('int8*'), + $this->context->getTypeFromString('size_t'), + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('uint32'), + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('uint32') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + '_erealloc', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 2 + 1, + $this->context->attributes['readonly'], + 0 + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 2 + 1, + $this->context->attributes['nocapture'], + 0 + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 4 + 1, + $this->context->attributes['readonly'], + 0 + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 4 + 1, + $this->context->attributes['nocapture'], + 0 + ); + + $this->context->registerFunction('_erealloc', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('void'), + false, + $this->context->getTypeFromString('int8*'), + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('uint32'), + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('uint32') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + '_efree', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 1 + 1, + $this->context->attributes['readonly'], + 0 + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 1 + 1, + $this->context->attributes['nocapture'], + 0 + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 3 + 1, + $this->context->attributes['readonly'], + 0 + ); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex( + 3 + 1, + $this->context->attributes['nocapture'], + 0 + ); + + $this->context->registerFunction('_efree', $fn___cfcd208495d565ef66e7dff9f98764da); + } else { + $fntype___c4ca4238a0b923820dcc509a6f75849b = $this->context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('size_t') + ); + $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->module->addFunction( + '_emalloc', + $fntype___c4ca4238a0b923820dcc509a6f75849b + ); + + $this->context->registerFunction('_emalloc', $fn___c4ca4238a0b923820dcc509a6f75849b); + + $fntype___c4ca4238a0b923820dcc509a6f75849b = $this->context->context->functionType( + $this->context->getTypeFromString('int8*'), + false, + $this->context->getTypeFromString('int8*'), + $this->context->getTypeFromString('size_t') + ); + $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->module->addFunction( + '_erealloc', + $fntype___c4ca4238a0b923820dcc509a6f75849b + ); + + $this->context->registerFunction('_erealloc', $fn___c4ca4238a0b923820dcc509a6f75849b); + + $fntype___c4ca4238a0b923820dcc509a6f75849b = $this->context->context->functionType( + $this->context->getTypeFromString('void'), + false, + $this->context->getTypeFromString('int8*') + ); + $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->module->addFunction( + '_efree', + $fntype___c4ca4238a0b923820dcc509a6f75849b + ); + + $this->context->registerFunction('_efree', $fn___c4ca4238a0b923820dcc509a6f75849b); + } + } + + public function implement(): void + { + if (\PHP_DEBUG) { + // FIXME: Use real values here, not constants. + + // These variables are a hack because compile{} + // blocks currently only accept variables as arguments. + $jit = $this->context->constantFromString("jit"); + $__type = $this->context->getTypeFromString('int32'); + + $__kind = $__type->getKind(); + $__value = 2; + switch ($__kind) { + case \PHPLLVM\Type::KIND_INTEGER: + if (!is_object($__value)) { + $two = $__type->constInt($__value, false); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + if ($__other_type->getWidth() >= $__type->getWidth()) { + $two = $this->context->builder->truncOrBitCast($__value, $__type); + } else { + $two = $this->context->builder->zExtOrBitCast($__value, $__type); + } + break; + case \PHPLLVM\Type::KIND_DOUBLE: + $two = $this->context->builder->fpToSi($__value, $__type); + + break; + case \PHPLLVM\Type::KIND_ARRAY: + case \PHPLLVM\Type::KIND_POINTER: + $two = $this->context->builder->ptrToInt($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (int, " . $__other_type->toString() . ")" + ); + } + break; + case \PHPLLVM\Type::KIND_DOUBLE: + if (!is_object($__value)) { + $two = $__type->constReal(2); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + $two = $this->context->builder->siToFp($__value, $__type); + + break; + case \PHPLLVM\Type::KIND_DOUBLE: + $two = $this->context->builder->fpCast($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (double, " . $__other_type->toString() . ")" + ); + } + break; + case \PHPLLVM\Type::KIND_ARRAY: + case \PHPLLVM\Type::KIND_POINTER: + if (!is_object($__value)) { + // this is very likely very wrong... + $two = $__type->constInt($__value, false); + break; + } + $__other_type = $__value->typeOf(); + switch ($__other_type->getKind()) { + case \PHPLLVM\Type::KIND_INTEGER: + $two = $this->context->builder->intToPtr($__value, $__type); + break; + case \PHPLLVM\Type::KIND_ARRAY: + // $__tmp = $this->context->builder->($__value, $this->context->context->int64Type()); + // $(result) = $this->context->builder->intToPtr($__tmp, $__type); + // break; + case \PHPLLVM\Type::KIND_POINTER: + $two = $this->context->builder->pointerCast($__value, $__type); + break; + default: + throw new \LogicException( + "Unknown how to handle type pair (double, " . $__other_type->toString() . ")" + ); + } + break; + default: + throw new \LogicException("Unsupported type cast: " . $__type->toString()); + } + + $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $this->context->lookupFunction('__mm__malloc'); + $block___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->appendBasicBlock( + 'main' + ); + $this->context->builder->positionAtEnd($block___eccbc87e4b5ce2fe28308fd9f2a7baf3); + $size = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->getParam(0); + + $result = $this->context->builder->call( + $this->context->lookupFunction('_emalloc'), + $size, + $jit, + $two, + $jit, + $two + ); + $this->context->builder->returnValue($result); + + $this->context->builder->clearInsertionPosition(); + $fn___e4da3b7fbbce2345d7772b0674a318d5 = $this->context->lookupFunction('__mm__realloc'); + $block___e4da3b7fbbce2345d7772b0674a318d5 = $fn___e4da3b7fbbce2345d7772b0674a318d5->appendBasicBlock( + 'main' + ); + $this->context->builder->positionAtEnd($block___e4da3b7fbbce2345d7772b0674a318d5); + $void = $fn___e4da3b7fbbce2345d7772b0674a318d5->getParam(0); + $size = $fn___e4da3b7fbbce2345d7772b0674a318d5->getParam(1); + + $result = $this->context->builder->call( + $this->context->lookupFunction('_erealloc'), + $void, + $size, + $jit, + $two, + $jit, + $two + ); + $this->context->builder->returnValue($result); + + $this->context->builder->clearInsertionPosition(); + $fn___8f14e45fceea167a5a36dedd4bea2543 = $this->context->lookupFunction('__mm__free'); + $block___8f14e45fceea167a5a36dedd4bea2543 = $fn___8f14e45fceea167a5a36dedd4bea2543->appendBasicBlock( + 'main' + ); + $this->context->builder->positionAtEnd($block___8f14e45fceea167a5a36dedd4bea2543); + $void = $fn___8f14e45fceea167a5a36dedd4bea2543->getParam(0); + + $this->context->builder->call($this->context->lookupFunction('_efree'), $void, $jit, $two, $jit, $two); + $this->context->builder->returnVoid(); + + $this->context->builder->clearInsertionPosition(); + } else { + $fn___45c48cce2e2d7fbdea1afc51c7c6ad26 = $this->context->lookupFunction('__mm__malloc'); + $block___45c48cce2e2d7fbdea1afc51c7c6ad26 = $fn___45c48cce2e2d7fbdea1afc51c7c6ad26->appendBasicBlock( + 'main' + ); + $this->context->builder->positionAtEnd($block___45c48cce2e2d7fbdea1afc51c7c6ad26); + $size = $fn___45c48cce2e2d7fbdea1afc51c7c6ad26->getParam(0); + + $result = $this->context->builder->call($this->context->lookupFunction('_emalloc'), $size); + $this->context->builder->returnValue($result); + + $this->context->builder->clearInsertionPosition(); + $fn___6512bd43d9caa6e02c990b0a82652dca = $this->context->lookupFunction('__mm__realloc'); + $block___6512bd43d9caa6e02c990b0a82652dca = $fn___6512bd43d9caa6e02c990b0a82652dca->appendBasicBlock( + 'main' + ); + $this->context->builder->positionAtEnd($block___6512bd43d9caa6e02c990b0a82652dca); + $void = $fn___6512bd43d9caa6e02c990b0a82652dca->getParam(0); + $size = $fn___6512bd43d9caa6e02c990b0a82652dca->getParam(1); + + $result = $this->context->builder->call($this->context->lookupFunction('_erealloc'), $void, $size); + $this->context->builder->returnValue($result); + + $this->context->builder->clearInsertionPosition(); + $fn___c51ce410c124a10e0db5e4b97fc2af39 = $this->context->lookupFunction('__mm__free'); + $block___c51ce410c124a10e0db5e4b97fc2af39 = $fn___c51ce410c124a10e0db5e4b97fc2af39->appendBasicBlock( + 'main' + ); + $this->context->builder->positionAtEnd($block___c51ce410c124a10e0db5e4b97fc2af39); + $void = $fn___c51ce410c124a10e0db5e4b97fc2af39->getParam(0); + + $this->context->builder->call($this->context->lookupFunction('_efree'), $void); + $this->context->builder->returnVoid(); + + $this->context->builder->clearInsertionPosition(); + } + } +} diff --git a/lib/JIT/Builtin/MemoryManager/PHP.pre b/lib/Builtin/MemoryManager/PHP.pre similarity index 100% rename from lib/JIT/Builtin/MemoryManager/PHP.pre rename to lib/Builtin/MemoryManager/PHP.pre diff --git a/lib/Builtin/Output.php b/lib/Builtin/Output.php new file mode 100644 index 0000000..40e542d --- /dev/null +++ b/lib/Builtin/Output.php @@ -0,0 +1,69 @@ +context->context->functionType( + $this->context->getTypeFromString('int32'), + true, + $this->context->getTypeFromString('char*') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + 'printf', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(0 + 1, $this->context->attributes['readonly'], 0); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(0 + 1, $this->context->attributes['nocapture'], 0); + + $this->context->registerFunction('printf', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('int32'), + true, + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('char*') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + 'sprintf', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['readonly'], 0); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['nocapture'], 0); + + $this->context->registerFunction('sprintf', $fn___cfcd208495d565ef66e7dff9f98764da); + + $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( + $this->context->getTypeFromString('int32'), + true, + $this->context->getTypeFromString('char*'), + $this->context->getTypeFromString('size_t'), + $this->context->getTypeFromString('char*') + ); + $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction( + 'snprintf', + $fntype___cfcd208495d565ef66e7dff9f98764da + ); + + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(2 + 1, $this->context->attributes['readonly'], 0); + $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(2 + 1, $this->context->attributes['nocapture'], 0); + + $this->context->registerFunction('snprintf', $fn___cfcd208495d565ef66e7dff9f98764da); + } +} diff --git a/lib/JIT/Builtin/Output.pre b/lib/Builtin/Output.pre similarity index 100% rename from lib/JIT/Builtin/Output.pre rename to lib/Builtin/Output.pre diff --git a/lib/JIT/Builtin/Refcount.php b/lib/Builtin/Refcount.php similarity index 99% rename from lib/JIT/Builtin/Refcount.php rename to lib/Builtin/Refcount.php index 5ee0d30..d288b89 100644 --- a/lib/JIT/Builtin/Refcount.php +++ b/lib/Builtin/Refcount.php @@ -1,7 +1,7 @@ context->location()); } */ -} +} \ No newline at end of file diff --git a/lib/JIT/Builtin/Type/String_.pre b/lib/Builtin/Type/String_.pre similarity index 100% rename from lib/JIT/Builtin/Type/String_.pre rename to lib/Builtin/Type/String_.pre diff --git a/lib/JIT/Builtin/Type/Value.php b/lib/Builtin/Type/Value.php similarity index 99% rename from lib/JIT/Builtin/Type/Value.php rename to lib/Builtin/Type/Value.php index b8e645f..6c36d8c 100644 --- a/lib/JIT/Builtin/Type/Value.php +++ b/lib/Builtin/Type/Value.php @@ -1,7 +1,7 @@ context->builder->clearInsertionPosition(); } -} +} \ No newline at end of file diff --git a/lib/JIT/Builtin/Type/Value.pre b/lib/Builtin/Type/Value.pre similarity index 100% rename from lib/JIT/Builtin/Type/Value.pre rename to lib/Builtin/Type/Value.pre diff --git a/lib/JIT/Builtin/VarArg.php b/lib/Builtin/VarArg.php similarity index 100% rename from lib/JIT/Builtin/VarArg.php rename to lib/Builtin/VarArg.php diff --git a/lib/JIT/Call.php b/lib/Call.php similarity index 92% rename from lib/JIT/Call.php rename to lib/Call.php index c9a690d..6cd0fd8 100644 --- a/lib/JIT/Call.php +++ b/lib/Call.php @@ -9,7 +9,7 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCompiler\JIT; +namespace PHPCompiler; use PHPLLVM\Value; diff --git a/lib/JIT/Call/Native.php b/lib/Call/Native.php similarity index 98% rename from lib/JIT/Call/Native.php rename to lib/Call/Native.php index ddc62c6..3c79a6e 100644 --- a/lib/JIT/Call/Native.php +++ b/lib/Call/Native.php @@ -1,7 +1,7 @@ type)); } -} +} \ No newline at end of file diff --git a/lib/JIT/Call/Native.pre b/lib/Call/Native.pre similarity index 96% rename from lib/JIT/Call/Native.pre rename to lib/Call/Native.pre index 1e64102..19c02fd 100755 --- a/lib/JIT/Call/Native.pre +++ b/lib/Call/Native.pre @@ -7,11 +7,11 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCompiler\JIT\Call; +namespace PHPCompiler\Call; -use PHPCompiler\JIT\Context; -use PHPCompiler\JIT\Call; -use PHPCompiler\JIT\Variable; +use PHPCompiler\Context; +use PHPCompiler\Call; +use PHPCompiler\Variable; use PHPLLVM\Value; diff --git a/lib/JIT/Call/Vararg.php b/lib/Call/Vararg.php similarity index 90% rename from lib/JIT/Call/Vararg.php rename to lib/Call/Vararg.php index 486b82c..727b0ef 100755 --- a/lib/JIT/Call/Vararg.php +++ b/lib/Call/Vararg.php @@ -7,11 +7,11 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCompiler\JIT\Call; +namespace PHPCompiler\Call; -use PHPCompiler\JIT\Context; -use PHPCompiler\JIT\Call; -use PHPCompiler\JIT\Variable; +use PHPCompiler\Context; +use PHPCompiler\Call; +use PHPCompiler\Variable; use PHPLLVM\Value; diff --git a/lib/Compiler.php b/lib/Compiler.php old mode 100755 new mode 100644 index ddf8913..276962a --- a/lib/Compiler.php +++ b/lib/Compiler.php @@ -1,5 +1,8 @@ seen = new SplObjectStorage; + public int $optimizationLevel = 3; - $main = $this->compileCfgBlock($script->main->cfg); - $this->seen = null; - return $main; - } + private array $stringConstant = []; + private array $intConstant = []; + private array $builtIns = []; - public function compileFunc(string $name, CfgFunc $func): Func { - $this->seen = new SplObjectStorage; + private array $queue = []; - $funcBlock = $this->compileCfgBlock($func->cfg, $func->params); - $funcBlock->func = $func; - $this->seen = null; - return new Func\PHP($name, $funcBlock); - } + public Context $context; - protected function compileCfgBlock(CfgBlock $block, array $params = []): Block { - if (!$this->seen->contains($block)) { - $this->seen[$block] = $new = new Block($block); - $paramIdx = 0; - foreach ($params as $param) { - $new->addOpCode($this->compileParam($param, $new, $paramIdx++)); - } - $this->compileBlock($new); - } - return $this->seen[$block]; + public function __construct(Context $context) { + $this->context = $context; } - protected function compileBlock(Block $block) { - $this->compileOps($block->orig->children, $block); + public function compile(Block $block): PHPLLVM\Value { + $return = $this->compileBlock($block); + $this->runQueue(); + return $return; } - protected function compileOps(array $ops, Block $block): void { - // First hoist functions and class definitions - foreach ($ops as $child) { - switch (get_class($child)) { - case Op\Stmt\Function_::class: - $block->addOpCode($this->compileFunction($child, $block)); - break; - case Op\Stmt\Class_::class: - case Op\Stmt\Interface_::class: - case Op\Stmt\Trait_::class: - $block->addOpCode($this->compileClassLike($child, $block)); - break; - } - } - foreach ($ops as $child) { - switch (get_class($child)) { - case Op\Stmt\Function_::class: - case Op\Stmt\Class_::class: - case Op\Stmt\Interface_::class: - case Op\Stmt\Trait_::class: - break; - default: - $this->compileOp($child, $block); - } + public function compileFunc(CoreFunc $func): void { + if ($func instanceof CoreFunc\PHP) { + $this->compileBlock($func->block, $func->getName()); + $this->runQueue(); + return; + } elseif ($func instanceof CoreFunc\JIT) { + // No need to do anything, already compiled + return; + } elseif ($func instanceof CoreFunc\Internal) { + $this->context->functionProxies[strtolower($func->getName())] = $func; + return; } + throw new \LogicException("Unknown func type encountered: " . get_class($func)); } - protected function compileClassLike(Op\Stmt\ClassLike $class, Block $block): OpCode { - $type = 0; - if ($class instanceof Op\Stmt\Class_) { - $type = OpCode::TYPE_DECLARE_CLASS; - assert(null === $class->extends); - assert(empty($class->implements)); - } else { - throw new \LogicException('Unsupported class type: ' . get_class($class)); + private function runQueue(): void { + while (!empty($this->queue)) { + $run = array_shift($this->queue); + $this->compileBlockInternal($run[0], $run[1], ...$run[2]); } - $return = new OpCode( - $type, - $this->compileOperand($class->name, $block, true) - ); - $return->block1 = $this->compileClassBody($class->stmts, $type); - return $return; } - protected function compileClassBody(CfgBlock $block, int $type): Block { - $result = new Block($block); - foreach ($block->children as $child) { - switch (get_class($child)) { - case Op\Stmt\Property::class: - if ($type !== OpCode::TYPE_DECLARE_CLASS) { - throw new \LogicException('Properties are only supported on classes for now'); - } - if (!is_null($child->defaultBlock)) { - $this->compileOps($child->defaultBlock, $result); - } - $result->addOpCode(new OpCode( - OpCode::TYPE_DECLARE_PROPERTY, - $this->compileOperand($child->name, $result, true), - is_null($child->defaultVar) ? null : $this->compileOperand($child->defaultVar, $result, true), - $this->compileTypeConstrainedVariable($result, $child->type) - )); - break; - default: - throw new \LogicException('Unsupported class body element: ' . get_class($child)); - } + private function compileBlock(Block $block, ?string $funcName = null): PHPLLVM\Value { + if (!is_null($funcName)) { + $internalName = $funcName; + } else { + $internalName = "internal_" . (++self::$functionNumber); } - return $result; - } + $args = []; + $rawTypes = []; + $argVars = []; + if (!is_null($block->func)) { + $callbackType = ''; + if ($block->func->returnType instanceof Op\Type\Literal) { + switch ($block->func->returnType->name) { + case 'void': + $callbackType = 'void'; + break; + case 'int': + $callbackType = 'long long'; + break; + case 'string': + $callbackType = '__string__*'; + break; + default: + throw new \LogicException("Non-void return types not supported yet"); + } + } else { + $callbackType = '__value__'; + } + $returnType = $this->context->getTypeFromString($callbackType); + $this->context->functionReturnType[strtolower($internalName)] = $callbackType; - protected function compileTypeConstrainedVariable(Block $block, Type $type): int { - $var = new Variable(Variable::TYPE_UNDEFINED); - $operand = new Operand\Temporary; - $operand->type = $type; - $return = $block->registerConstant($operand, $var); - $mappedType = Variable::mapFromType($type); - if ($mappedType === Variable::TYPE_UNDEFINED) { - // Mixed - return $return; - } elseif ($mappedType === Variable::TYPE_OBJECT) { - $var->classConstraint = $type->userType; + $callbackType .= '(*)('; + $callbackSep = ''; + foreach ($block->func->params as $idx => $param) { + if (empty($param->result->usages)) { + // only compile for param + assert($param->declaredType instanceof Op\Type\Literal); + $rawType = Type::fromDecl($param->declaredType->name); + } else { + $rawType = $param->result->type; + } + $type = $this->context->getTypeFromType($rawType); + $callbackType .= $callbackSep . $this->context->getStringFromType($type); + $callbackSep = ', '; + $rawTypes[] = $rawType; + $args[] = $type; + } + $callbackType .= ')'; + } else { + $callbackType = 'void(*)()'; + $returnType = $this->context->getTypeFromString('void'); } - $var->typeConstraint = $mappedType; - return $return; - } + $isVarArgs = false; - protected function compileParam(Op\Expr\Param $param, Block $block, int $paramIdx): OpCode { - assert(false === $param->byRef); - assert(false === $param->variadic); - assert(null === $param->defaultBlock); - return new OpCode( - OpCode::TYPE_ARG_RECV, - $this->compileOperand($param->result, $block, false), - $paramIdx + $func = $this->context->module->addFunction( + $internalName, + $this->context->context->functionType( + $returnType, + $isVarArgs, + ...$args + ) ); - } - - protected function compileFunction(Op\Stmt\Function_ $function, Block $block): OpCode { - $funcBlock = $this->compileCfgBlock($function->func->cfg, $function->func->params); - $funcBlock->func = $function->func; - $operand = new Operand\Literal($function->func->name); - $operand->type = Type::string(); - $return = new OpCode( - OpCode::TYPE_FUNCDEF, - $this->compileOperand($operand, $block, true) - ); - $return->block1 = $funcBlock; - return $return; - } - protected function compileOp(Op $op, Block $block) { - if ($op instanceof Op\Expr\ConcatList) { - $total = count($op->list); - assert($total >= 2); - $pointer = 2; - - $return = $this->compileOperand($op->result, $block, false); - $block->addOpCode(new OpCode( - OpCode::TYPE_CONCAT, - $return, - $this->compileOperand($op->list[0], $block, true), - $this->compileOperand($op->list[1], $block, true) - )); - while ($pointer < $total) { - $right = $this->compileOperand($op->list[$pointer++], $block, true); - $block->addOpCode(new OpCode( - OpCode::TYPE_CONCAT, - $return, - $return, - $right - )); - } - } elseif ($op instanceof Op\Expr) { - $block->addOpCode(...$this->compileExpr($op, $block)); - } elseif ($op instanceof Op\Stmt) { - $this->compileStmt($op, $block); - } elseif ($op instanceof Op\Terminal) { - $block->addOpCode($this->compileTerminal($op, $block)); - } else { - throw new \LogicException("Unknown Op Type: " . $op->getType()); + foreach ($args as $idx => $arg) { + $argVars[] = new Variable($this->context, Variable::getTypeFromType($rawTypes[$idx]), Variable::KIND_VALUE, $func->getParam($idx)); } - } - protected function compileStmt(Op\Stmt $stmt, Block $block) { - if ($stmt instanceof Op\Stmt\Jump) { - $op = new OpCode(OpCode::TYPE_JUMP); - $op->block1 = $this->compileCfgBlock($stmt->target); - $op->block1->parents[] = $block; - $block->addOpCode($op); - } elseif ($stmt instanceof Op\Stmt\JumpIf) { - $op = new OpCode(OpCode::TYPE_JUMPIF, $this->compileOperand($stmt->cond, $block, true)); - $op->block1 = $this->compileCfgBlock($stmt->if); - $op->block2 = $this->compileCfgBlock($stmt->else); - $op->block1->parents[] = $block; - $op->block2->parents[] = $block; - $block->addOpCode($op); - } elseif ($stmt instanceof Op\Stmt\Switch_) { - $canBeSwitch = true; - $type = null; - foreach ($stmt->cases as $case) { - if (!$case instanceof Operand\Literal) { - $canBeSwitch = false; - break; - } - if (is_null($type)) { - $type = $case->type; - } elseif (!$type->equals($case->type)) { - $canBeSwitch = false; - } - } - if ($canBeSwitch) { - $this->compileSwitchStmt($stmt, $block); + if (!is_null($funcName)) { + $lcname = strtolower($funcName); + $this->context->functions[$lcname] = $func; + if ($isVarArgs) { + $this->context->functionProxies[$lcname] = new JIT\Call\Vararg($func, $funcName, count($args)); } else { - $this->compileSwitchToIfBlocks($stmt, $block); + $this->context->functionProxies[$lcname] = new JIT\Call\Native($func, $funcName, $args); } - } else { - throw new \LogicException("Unknown Stmt Type: " . $stmt->getType()); } - } - protected function compileSwitchStmt(Op\Stmt\Switch_ $switch, Block $block): void { - $op = $this->compileOperand($switch->cond, $block, true); - foreach ($switch->cases as $key => $case) { - $caseOp = new OpCode( - OpCode::TYPE_CASE, - $op, - $this->compileOperand($case, $block, true) - ); - $caseOp->block1 = $this->compileCfgBlock($switch->targets[$key]); - $caseOp->block1->parents[] = $block; - $block->addOpCode($caseOp); + $this->queue[] = [$func, $block, $argVars]; + if ($callbackType === 'void(*)()') { + $this->context->addExport($internalName, $callbackType, $block); } - $defaultOp = new OpCode(OpCode::TYPE_JUMP); - $defaultOp->block1 = $this->compileCfgBlock($switch->default); - $defaultOp->block1->parents[] = $block; - $block->addOpCode($defaultOp); + return $func; } - - protected function getOpCodeTypeFromBinaryOp(Op\Expr\BinaryOp $expr): int { - if ($expr instanceof Op\Expr\BinaryOp\Concat) { - return OpCode::TYPE_CONCAT; - } elseif ($expr instanceof Op\Expr\BinaryOp\Plus) { - return OpCode::TYPE_PLUS; - } elseif ($expr instanceof Op\Expr\BinaryOp\Smaller) { - return OpCode::TYPE_SMALLER; - } elseif ($expr instanceof Op\Expr\BinaryOp\Greater) { - return OpCode::TYPE_GREATER; - } elseif ($expr instanceof Op\Expr\BinaryOp\SmallerOrEqual) { - return OpCode::TYPE_SMALLER_OR_EQUAL; - } elseif ($expr instanceof Op\Expr\BinaryOp\GreaterOrEqual) { - return OpCode::TYPE_GREATER_OR_EQUAL; - } elseif ($expr instanceof Op\Expr\BinaryOp\Equal) { - return OpCode::TYPE_EQUAL; - } elseif ($expr instanceof Op\Expr\BinaryOp\Identical) { - return OpCode::TYPE_IDENTICAL; - } elseif ($expr instanceof Op\Expr\BinaryOp\Minus) { - return OpCode::TYPE_MINUS; - } elseif ($expr instanceof Op\Expr\BinaryOp\Mul) { - return OpCode::TYPE_MUL; - } elseif ($expr instanceof Op\Expr\BinaryOp\Div) { - return OpCode::TYPE_DIV; - } elseif ($expr instanceof Op\Expr\BinaryOp\Mod) { - return OpCode::TYPE_MODULO; - } elseif ($expr instanceof Op\Expr\BinaryOp\BitwiseAnd) { - return OpCode::TYPE_BITWISE_AND; - } elseif ($expr instanceof Op\Expr\BinaryOp\BitwiseOr) { - return OpCode::TYPE_BITWISE_OR; - } elseif ($expr instanceof Op\Expr\BinaryOp\BitwiseXor) { - return OpCode::TYPE_BITWISE_XOR; + + private function compileBlockInternal( + PHPLLVM\Value $func, + Block $block, + Variable ...$args + ): PHPLLVM\BasicBlock { + if ($this->context->scope->blockStorage->contains($block)) { + return $this->context->scope->blockStorage[$block]; } - throw new \LogicException("Unknown BinaryOp Type: " . $expr->getType()); - } - - protected function getOpCodeTypeFromCastOp(Op\Expr\Cast $expr): int { - if ($expr instanceof Op\Expr\Cast\Array_) { - return OpCode::TYPE_CAST_ARRAY; - } elseif ($expr instanceof Op\Expr\Cast\Bool_) { - return OpCode::TYPE_CAST_BOOL; - } elseif ($expr instanceof Op\Expr\Cast\Double) { - return OpCode::TYPE_CAST_FLOAT; - } elseif ($expr instanceof Op\Expr\Cast\Int_) { - return OpCode::TYPE_CAST_INT; - } elseif ($expr instanceof Op\Expr\Cast\Object_) { - return OpCode::TYPE_CAST_OBJECT; - } elseif ($expr instanceof Op\Expr\Cast\String_) { - return OpCode::TYPE_CAST_STRING; - } elseif ($expr instanceof Op\Expr\Cast\Unset_) { - return OpCode::TYPE_CAST_UNSET; + self::$blockNumber++; + $origBasicBlock = $basicBlock = $func->appendBasicBlock('block_' . self::$blockNumber); + $this->context->scope->blockStorage[$block] = $basicBlock; + $builder = $this->context->builder; + $builder->positionAtEnd($basicBlock); + // Handle hoisted variables + foreach ($block->orig->hoistedOperands as $operand) { + $this->context->makeVariableFromOp($func, $basicBlock, $block, $operand); } - throw new \LogicException("Unknown CastOp Type: " . $expr->getType()); - } - protected function getOpCodeTypeFromUnaryOp(Op\Expr $expr): int { - if ($expr instanceof Op\Expr\UnaryMinus) { - return OpCode::TYPE_UNARY_MINUS; - } elseif ($expr instanceof Op\Expr\UnaryPlus) { - return OpCode::TYPE_UNARY_PLUS; - } elseif ($expr instanceof Op\Expr\BitwiseNot) { - return OpCode::TYPE_BITWISE_NOT; - } elseif ($expr instanceof Op\Expr\BooleanNot) { - return OpCode::TYPE_BOOLEAN_NOT; - } elseif ($expr instanceof Op\Expr\Clone_) { - return OpCode::TYPE_CLONE; - } elseif ($expr instanceof Op\Expr\Empty_) { - return OpCode::TYPE_EMPTY; - } elseif ($expr instanceof Op\Expr\Eval_) { - return OpCode::TYPE_EVAL; - } elseif ($expr instanceof Op\Expr\Exit_) { - return OpCode::TYPE_EXIT; - } elseif ($expr instanceof Op\Expr\Print_) { - return OpCode::TYPE_PRINT; - } - throw new \LogicException("Unknown UnaryOp Type: " . $expr->getType()); - } + for ($i = 0, $length = count($block->opCodes); $i < $length; $i++) { + $op = $block->opCodes[$i]; + switch ($op->type) { + case OpCode::TYPE_ARG_RECV: + $this->assignOperand($block->getOperand($op->arg1), $args[$op->arg2]); + break; + case OpCode::TYPE_ASSIGN: + $value = $this->context->getVariableFromOp($block->getOperand($op->arg3)); + $this->assignOperand($block->getOperand($op->arg2), $value); + $this->assignOperand($block->getOperand($op->arg1), $value); + break; + // case OpCode::TYPE_ARRAY_DIM_FETCH: + // $value = $this->context->getVariableFromOp($block->getOperand($op->arg2)); + // $dimOp = $block->getOperand($op->arg3); + // $dim = $this->context->getVariableFromOp($dimOp); + // if ($value->type & Variable::IS_NATIVE_ARRAY && $this->context->analyzer->needsBoundsCheck($value, $dimOp)) { + // // compile bounds check + // $builder->call( + // $this->context->lookupFunction('__nativearray__boundscheck'), + // $dim->value, + // $this->context->constantFromInteger($value->nextFreeElement) + // ); + // } + // $this->assignOperand( + // $block->getOperand($op->arg1), + // $value->dimFetch($dim) + // ); + // break; + // case OpCode::TYPE_INIT_ARRAY: + // case OpCode::TYPE_ADD_ARRAY_ELEMENT: + // $result = $this->context->getVariableFromOp($block->getOperand($op->arg1)); + // if ($result->type & Variable::IS_NATIVE_ARRAY) { + // if (is_null($op->arg3)) { + // $idx = $result->nextFreeElement; + // } else { + // // this is safe, since we only compile to native array if it's checked to be good + // $idx = $block->getOperand($op->arg3)->value; + // } + // $this->context->helper->assign( + // $gccBlock, + // \gcc_jit_context_new_array_access( + // $this->context->context, + // $this->context->location(), + // $result->rvalue, + // $this->context->constantFromInteger($idx, 'size_t') + // ), + // $this->context->getVariableFromOp($block->getOperand($op->arg2))->rvalue + // ); + // $result->nextFreeElement = max($result->nextFreeElement, $idx + 1); + // } else { + // throw new \LogicException('Hash tables not implemented yet'); + // } + // break; + case OpCode::TYPE_BOOLEAN_NOT: + $from = $this->context->getVariableFromOp($block->getOperand($op->arg2)); + if ($from->type === Variable::TYPE_NATIVE_BOOL) { + $value = $this->context->helper->loadValue($from); + } else { + $value = $this->context->castToBool($this->context->helper->loadValue($from)); + } + $__right = $value->typeOf()->constInt(1, false); + + - protected function compileExpr(Op\Expr $expr, Block $block): array { - if ($expr instanceof Op\Expr\BinaryOp) { - return [new OpCode( - $this->getOpCodeTypeFromBinaryOp($expr), - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->left, $block, true), - $this->compileOperand($expr->right, $block, true), - )]; - } elseif ($expr instanceof Op\Expr\Cast) { - return [new OpCode( - $this->getOpCodeTypeFromCastOp($expr), - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->expr, $block, true), - )]; - } - switch (get_class($expr)) { - case Op\Expr\Assertion::class: - if ($expr->result instanceof Operand\Literal) { - //short circuit - return []; - } elseif ($expr->expr === $expr->result) { - return []; - } - return [new OpCode( - OpCode::TYPE_TYPE_ASSERT, - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->expr, $block, true) - )]; - case Op\Expr\Assign::class: - return [new OpCode( - OpCode::TYPE_ASSIGN, - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->var, $block, false), - $this->compileOperand($expr->expr, $block, true) - )]; - case Op\Expr\UnaryMinus::class: - case Op\Expr\UnaryPlus::class: - case Op\Expr\BitwiseNot::class: - case Op\Expr\BooleanNot::class: - case Op\Expr\Clone_::class: - case Op\Expr\Empty_::class: - case Op\Expr\Eval_::class: - case Op\Expr\Exit_::class: - case Op\Expr\Print_::class: - return [new OpCode( - $this->getOpCodeTypeFromUnaryOp($expr), - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->expr, $block, true) - )]; - case Op\Expr\ArrayDimFetch::class: - return [new OpCode( - OpCode::TYPE_ARRAY_DIM_FETCH, - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->var, $block, true), - $this->compileOperand($expr->dim, $block, true) - )]; - case Op\Expr\ConstFetch::class: - $nsName = null; - if (!is_null($expr->nsName)) { - $nsName = $this->compileOperand($expr->nsName, $block, true); - } - return [new OpCode( - OpCode::TYPE_CONST_FETCH, - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->name, $block, true), - $nsName - )]; - case Op\Expr\FuncCall::class: - $return = [ - new OpCode( - OpCode::TYPE_FUNCCALL_INIT, - $this->compileOperand($expr->name, $block, true) - ) - ]; - foreach ($expr->args as $arg) { - $return[] = new OpCode( - OpCode::TYPE_ARG_SEND, - $this->compileOperand($arg, $block, true) - ); - } - if (!empty($expr->result->usages)) { - $return[] = new OpCode( - OpCode::TYPE_FUNCCALL_EXEC_RETURN, - $this->compileOperand($expr->result, $block, false) + + + + + $result = $this->context->builder->bitwiseXor($value, $__right); + + + $this->assignOperandValue($block->getOperand($op->arg1), $result); + break; + case OpCode::TYPE_CONCAT: + if (!$this->context->hasVariableOp($block->getOperand($op->arg1))) { + // don't bother with constant operations + break; + } + $result = $this->context->getVariableFromOp($block->getOperand($op->arg1)); + $left = $this->context->getVariableFromOp($block->getOperand($op->arg2)); + $right = $this->context->getVariableFromOp($block->getOperand($op->arg3)); + $this->context->type->string->concat($result, $left, $right); + break; + case OpCode::TYPE_CONST_FETCH: + $value = null; + if (!is_null($op->arg3)) { + // try NS constant fetch + $value = $this->context->constantFetch($block->getOperand($op->arg3)); + } + if (is_null($value)) { + $value = $this->context->constantFetch($block->getOperand($op->arg2)); + } + if (is_null($value)) { + throw new \RuntimeException('Unknown constant fetch'); + } + $this->assignOperand($block->getOperand($op->arg1), $value); + break; + case OpCode::TYPE_CAST_BOOL: + $value = $this->context->getVariableFromOp($block->getOperand($op->arg2)); + $this->assignOperand($block->getOperand($op->arg1), $value->castTo(Variable::TYPE_NATIVE_BOOL)); + break; + case OpCode::TYPE_ECHO: + case OpCode::TYPE_PRINT: + $argOffset = $op->type === OpCode::TYPE_ECHO ? $op->arg1 : $op->arg2; + $arg = $this->context->getVariableFromOp($block->getOperand($argOffset)); + $argValue = $this->context->helper->loadValue($arg); + switch ($arg->type) { + case Variable::TYPE_VALUE: + $argValue = $this->context->builder->call( + $this->context->lookupFunction('__value__readString') , + $argValue + ); - } else { - $return[] = new OpCode( - OpCode::TYPE_FUNCCALL_EXEC_NORETURN, + + // Fall through intentional + case Variable::TYPE_STRING: + $fmt = $this->context->builder->pointerCast( + $this->context->constantFromString("%.*s"), + $this->context->getTypeFromString('char*') ); - } - return $return; - case Op\Expr\StaticCall::class: - $return = [ - new OpCode( - OpCode::TYPE_STATICCALL_INIT, - $this->compileOperand($expr->class, $block, true), - $this->compileOperand($expr->name, $block, true) - ) - ]; - foreach ($expr->args as $arg) { - $return[] = new OpCode( - OpCode::TYPE_ARG_SEND, - $this->compileOperand($arg, $block, true) + $offset = $this->context->structFieldMap[$argValue->typeOf()->getElementType()->getName()]['length']; + $__str__length = $this->context->builder->load( + $this->context->builder->structGep($argValue, $offset) ); - } - if (!empty($expr->result->usages)) { - $return[] = new OpCode( - OpCode::TYPE_FUNCCALL_EXEC_RETURN, - $this->compileOperand($expr->result, $block, false) + $offset = $this->context->structFieldMap[$argValue->typeOf()->getElementType()->getName()]['value']; + $__str__value = $this->context->builder->structGep($argValue, $offset); + $this->context->builder->call( + $this->context->lookupFunction('printf') , + $fmt + , $__str__length + , $__str__value + + ); + + break; + case Variable::TYPE_NATIVE_LONG: + $fmt = $this->context->builder->pointerCast( + $this->context->constantFromString("%lld"), + $this->context->getTypeFromString('char*') ); - } else { - $return[] = new OpCode( - OpCode::TYPE_FUNCCALL_EXEC_NORETURN, + $this->context->builder->call( + $this->context->lookupFunction('printf') , + $fmt + , $argValue + + ); + + break; + case Variable::TYPE_NATIVE_DOUBLE: + $fmt = $this->context->builder->pointerCast( + $this->context->constantFromString("%G"), + $this->context->getTypeFromString('char*') ); - } - return $return; - case Op\Expr\New_::class: - $return = [ - new OpCode( - OpCode::TYPE_NEW, - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->class, $block, true), - ) - ]; - foreach ($expr->args as $arg) { - $return[] = new OpCode( - OpCode::TYPE_ARG_SEND, - $this->compileOperand($arg, $block, true) + $this->context->builder->call( + $this->context->lookupFunction('printf') , + $fmt + , $argValue + + ); + + break; + case Variable::TYPE_NATIVE_BOOL: + $bool = $this->context->castToBool($argValue); + $prev = $this->context->builder->getInsertBlock(); + $ifBlock = $prev->insertBasicBlock('ifBlock'); + $prev->moveBefore($ifBlock); + + $endBlock[] = $tmp = $ifBlock->insertBasicBlock('endBlock'); + $this->context->builder->branchIf($bool, $ifBlock, $tmp); + + $this->context->builder->positionAtEnd($ifBlock); + { $fmt = $this->context->builder->pointerCast( + $this->context->constantFromString("1"), + $this->context->getTypeFromString('char*') ); - } - $return[] = new OpCode( - OpCode::TYPE_FUNCCALL_EXEC_NORETURN + $this->context->builder->call( + $this->context->lookupFunction('printf') , + $fmt + ); - return $return; - case Op\Expr\PropertyFetch::class: - return [new OpCode( - OpCode::TYPE_PROPERTY_FETCH, - $this->compileOperand($expr->result, $block, false), - $this->compileOperand($expr->var, $block, true), - $this->compileOperand($expr->name, $block, true) - )]; - case Op\Expr\Array_::class: - $result = $this->compileOperand($expr->result, $block, false); - if (empty($expr->values)) { - return [new OpCode( - OpCode::TYPE_INIT_ARRAY, - $result - )]; + } + if ($this->context->builder->getInsertBlock()->getTerminator() === null) { + $this->context->builder->branch(end($endBlock)); } - $return = [new OpCode( - OpCode::TYPE_INIT_ARRAY, - $result, - $this->compileOperand($expr->values[0], $block, true), - $this->compileOperand($expr->keys[0], $block, true) - )]; - for ($i = 1, $n = count($expr->values); $i < $n; $i++) { - $return[] = new OpCode( - OpCode::TYPE_ADD_ARRAY_ELEMENT, - $result, - $this->compileOperand($expr->values[$i], $block, true), - $this->compileOperand($expr->keys[$i], $block, true) + + $this->context->builder->positionAtEnd(array_pop($endBlock)); + + break; + + default: + throw new \LogicException("Echo for type $arg->type not implemented"); + } + if ($op->type === OpCode::TYPE_PRINT) { + $this->assignOperand( + $block->getOperand($op->arg1), + new Variable($this->context, Variable::TYPE_NATIVE_LONG, Variable::KIND_VALUE, $this->context->constantFromInteger(1)) + ); + } + break; + case OpCode::TYPE_MUL: + case OpCode::TYPE_PLUS: + case OpCode::TYPE_MINUS: + case OpCode::TYPE_DIV: + case OpCode::TYPE_MODULO: + case OpCode::TYPE_BITWISE_AND: + case OpCode::TYPE_BITWISE_OR: + case OpCode::TYPE_BITWISE_XOR: + case OpCode::TYPE_GREATER_OR_EQUAL: + case OpCode::TYPE_SMALLER_OR_EQUAL: + case OpCode::TYPE_GREATER: + case OpCode::TYPE_SMALLER: + case OpCode::TYPE_IDENTICAL: + case OpCode::TYPE_EQUAL: + $this->assignOperand( + $block->getOperand($op->arg1), + $this->context->helper->binaryOp( + $op, + $this->context->getVariableFromOp($block->getOperand($op->arg2)), + $this->context->getVariableFromOp($block->getOperand($op->arg3)) + ) + ); + break; + case OpCode::TYPE_UNARY_MINUS: + $this->assignOperand( + $block->getOperand($op->arg1), + $this->context->helper->unaryOp( + $op, + $this->context->getVariableFromOp($block->getOperand($op->arg2)), + ) + ); + break; + // case OpCode::TYPE_CASE: + case OpCode::TYPE_JUMP: + $newBlock = $this->compileBlockInternal($func, $op->block1, ...$args); + $builder->positionAtEnd($basicBlock); + $this->context->freeDeadVariables($func, $basicBlock, $block); + $builder->branch($newBlock); + return $origBasicBlock; + case OpCode::TYPE_JUMPIF: + $if = $this->compileBlockInternal($func, $op->block1, ...$args); + $else = $this->compileBlockInternal($func, $op->block2, ...$args); + + $builder->positionAtEnd($basicBlock); + + $condition = $this->context->castToBool( + $this->context->helper->loadValue($this->context->getVariableFromOp($block->getOperand($op->arg1))) ); - } - return $return; - } - throw new \LogicException("Unsupported expression: " . $expr->getType()); - } - protected function compileOperand(Operand $operand, Block $block, bool $isRead): ?int { - if ($operand instanceof Operand\NullOperand) { - return null; - } elseif ($operand instanceof Operand\Literal) { - assert($isRead === true); - $return = new Variable(Variable::mapFromType($operand->type)); - switch (Variable::mapFromType($operand->type)) { - case Variable::TYPE_STRING: - $return->string($operand->value); + $this->context->freeDeadVariables($func, $basicBlock, $block); + $builder->branchIf($condition, $if, $else); + return $origBasicBlock; + case OpCode::TYPE_RETURN_VOID: + $this->context->freeDeadVariables($func, $basicBlock, $block); + $this->context->builder->returnVoid(); + + return $origBasicBlock; + case OpCode::TYPE_RETURN: + $return = $this->context->getVariableFromOp($block->getOperand($op->arg1)); + $return->addref(); + $retval = $this->context->helper->loadValue($return); + $this->context->freeDeadVariables($func, $basicBlock, $block); + $this->context->builder->returnValue($retval); + + return $origBasicBlock; + case OpCode::TYPE_FUNCDEF: + $nameOp = $block->getOperand($op->arg1); + assert($nameOp instanceof Operand\Literal); + $this->compileBlock($op->block1, $nameOp->value); + break; + case OpCode::TYPE_FUNCCALL_INIT: + $nameOp = $block->getOperand($op->arg1); + if (!$nameOp instanceof Operand\Literal) { + throw new \LogicException("Variable function calls not yet supported"); + } + $lcname = strtolower($nameOp->value); + if (isset($this->context->functionProxies[$lcname])) { + $this->context->scope->toCall = $this->context->functionProxies[$lcname]; + } else { + throw new \RuntimeException("Call to undefined function $lcname"); + } + $this->context->scope->args = []; + break; + case OpCode::TYPE_ARG_SEND: + $this->context->scope->args[] = $this->context->getVariableFromOp($block->getOperand($op->arg1)); break; - case Variable::TYPE_INTEGER: - $return->int($operand->value); + case OpCode::TYPE_FUNCCALL_EXEC_NORETURN: + if (is_null($this->context->scope->toCall)) { + // short circuit + break; + } + $this->context->scope->toCall->call($this->context, ...$this->context->scope->args); break; - case Variable::TYPE_FLOAT: - $return->float($operand->value); + case OpCode::TYPE_FUNCCALL_EXEC_RETURN: + $result = $this->context->scope->toCall->call($this->context, ...$this->context->scope->args); + $this->assignOperandValue($block->getOperand($op->arg1), $result); break; - case Variable::TYPE_BOOLEAN: - $return->bool($operand->value); + // case OpCode::TYPE_DECLARE_CLASS: + // $this->context->pushScope(); + // $this->context->scope->classId = $this->context->type->object->declareClass($block->getOperand($op->arg1)); + // $this->compileClass($op->block1, $this->context->scope->classId); + // $this->context->popScope(); + // break; + // case OpCode::TYPE_NEW: + // $class = $this->context->type->object->lookupOperand($block->getOperand($op->arg2)); + // $this->context->helper->assign( + // $gccBlock, + // $this->context->getVariableFromOp($block->getOperand($op->arg1))->lvalue, + // $this->context->type->object->allocate($class) + // ); + // $this->context->scope->toCall = null; + // $this->context->scope->args = []; + // break; + // case OpCode::TYPE_PROPERTY_FETCH: + // $result = $block->getOperand($op->arg1); + // $obj = $block->getOperand($op->arg2); + // $name = $block->getOperand($op->arg3); + // assert($name instanceof Operand\Literal); + // assert($obj->type->type === Type::TYPE_OBJECT); + // $this->context->scope->variables[$result] = $this->context->type->object->propertyFetch( + // $this->context->getVariableFromOp($obj)->rvalue, + // $obj->type->userType, + // $name->value + // ); + // break; + default: + throw new \LogicException("Unknown JIT opcode: ". $op->getType()); + } + } + throw new \LogicException("Reached the end of the loop, this shouldn't happen..."); + } + + private function compileClass(?Block $block, int $classId) { + if ($block === null) { + return; + } + foreach ($block->opCodes as $op) { + switch ($op->type) { + case OpCode::TYPE_DECLARE_PROPERTY: + $name = $block->getOperand($op->arg1); + assert($name instanceof Operand\Literal); + assert(is_null($op->arg2)); // no defaults for now + $type = Variable::getTypeFromType($block->getOperand($op->arg3)->type); + $this->context->type->object->defineProperty($classId, $name->value, $type); break; default: - throw new \LogicException("Unknown Literal Operand Type: " . $operand->type); + var_dump($op); + throw new \LogicException('Other class body types are not jittable for now'); } - return $block->registerConstant($operand, $return); - } elseif ($operand instanceof Operand\Temporary) { - return $block->getVarSlot($operand, $isRead); + } - throw new \LogicException("Unknown Operand Type: " . $operand->getType()); } - protected function compileTerminal(Op\Terminal $terminal, Block $block): OpCode { - switch ($terminal->getType()) { - case 'Terminal_Echo': - $var = $this->compileOperand($terminal->expr, $block, true); - return new OpCode( - OpCode::TYPE_ECHO, - $var + private function assignOperand(Operand $result, Variable $value): void { + if (empty($result->usages) && !$this->context->scope->variables->contains($result)) { + return; + } + if (!$this->context->hasVariableOp($result)) { + // it's a kind! + $this->context->makeVariableFromValueOp($this->context->helper->loadValue($value), $result); + return; + } + $result = $this->context->getVariableFromOp($result); + if ($result->kind !== Variable::KIND_VARIABLE) { + throw new \LogicException("Cannot assign to a value"); + } + if ($value->type === $result->type) { + $result->free(); + if ($value->type & Variable::IS_NATIVE_ARRAY) { + // copy over the nextfreelement + //$result->nextFreeElement = $value->nextFreeElement; + } + $this->context->builder->store( + $this->context->helper->loadValue($value), + $result->value + ); + $result->addref(); + return; + } elseif ($result->type === Variable::TYPE_VALUE) { + // wrap + $valueRef = $result->value; + $valueFrom = $value->value; + switch ($value->type) { + case Variable::TYPE_NULL: + $this->context->builder->call( + $this->context->lookupFunction('__value__writeNull') , + $valueRef + ); - case 'Terminal_Return': - if (is_null($terminal->expr)) { - return new OpCode( - OpCode::TYPE_RETURN_VOID - ); - } - return new OpCode( - OpCode::TYPE_RETURN, - $this->compileOperand($terminal->expr, $block, true) + + return; + case Variable::TYPE_NATIVE_LONG: + $this->context->builder->call( + $this->context->lookupFunction('__value__writeLong') , + $valueRef + , $valueFrom + + ); + + return; + case Variable::TYPE_NATIVE_DOUBLE: + $this->context->builder->call( + $this->context->lookupFunction('__value__writeDouble') , + $valueRef + , $valueFrom + ); - default: - throw new \LogicException("Unknown Terminal Type: " . $terminal->getType()); + + return; + default: + throw new \LogicException("Source type: {$value->type}"); + } } + throw new \LogicException("Cannot assign operands of different types (yet): {$value->type}, {$result->type}"); + } + + private function assignOperandValue(Operand $result, PHPLLVM\Value $value): void { + if (empty($result->usages) && !$this->context->scope->variables->contains($result)) { + return; + } + if (!$this->context->hasVariableOp($result)) { + // it's a kind! + $this->context->makeVariableFromValueOp($value, $result); + return; + } + $result = $this->context->getVariableFromOp($result); + if ($result->kind !== Variable::KIND_VARIABLE) { + throw new \LogicException("Cannot assign to a value"); + } + $result->free(); + + $this->context->builder->store( + $value, + $result->value + ); + $result->addref(); } -} +} \ No newline at end of file diff --git a/lib/JIT.pre b/lib/Compiler.pre similarity index 99% rename from lib/JIT.pre rename to lib/Compiler.pre index f599ec3..6058556 100755 --- a/lib/JIT.pre +++ b/lib/Compiler.pre @@ -12,14 +12,12 @@ namespace PHPCompiler; use PHPCfg\Operand; use PHPCfg\Op; use PHPTypes\Type; -use PHPCompiler\JIT\Context; -use PHPCompiler\JIT\Variable; - -use PHPCompiler\Func as CoreFunc; +use PHPCompiler\Context; +use PHPCompiler\Variable; use PHPLLVM; -class JIT { +class Compiler { private static int $functionNumber = 0; private static int $blockNumber = 0; diff --git a/lib/JIT/Context.php b/lib/Context.php similarity index 100% rename from lib/JIT/Context.php rename to lib/Context.php diff --git a/lib/VM/Optimizer.php b/lib/FileResolver.php old mode 100755 new mode 100644 similarity index 57% rename from lib/VM/Optimizer.php rename to lib/FileResolver.php index a6063af..b978282 --- a/lib/VM/Optimizer.php +++ b/lib/FileResolver.php @@ -1,19 +1,16 @@ loadFrom($dir); + } + $dir = realpath($dir . '/..'); + } + throw new \LogicException("Could not find a valid composer install in folder tree"); + } + + protected function loadFrom(string $dir): array { + $result = []; + $files = require $dir . '/vendor/composer/autoload_files.php'; + foreach ($files as $file) { + $result[] = realpath($file); + } + $classmap = require $dir . '/vendor/composer/autoload_classmap.php'; + foreach ($classmap as $file) { + $result[] = realpath($file); + } + $classPaths = array_merge( + array_values(require $dir . '/vendor/composer/autoload_namespaces.php'), + array_values(require $dir . '/vendor/composer/autoload_psr4.php') + ); + $result = array_merge($result, $this->loadClassPaths($classPaths)); + $result = array_unique($result); + return $result; + } + + protected function loadClassPaths(array $paths): array { + $result = []; + foreach ($paths as $pathset) { + foreach ($pathset as $path) { + if (!is_dir($path)) { + continue; + } + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $path + ) + ); + foreach ($it as $file) { + if (in_array($file->getExtension(), $this->extensions)) { + $result[] = realpath($file->getPathname()); + } + } + } + } + return $result; + } + +} \ No newline at end of file diff --git a/lib/FileResolver/Directory.php b/lib/FileResolver/Directory.php new file mode 100644 index 0000000..b270af1 --- /dev/null +++ b/lib/FileResolver/Directory.php @@ -0,0 +1,38 @@ +getExtension(), $this->extensions)) { + $result[] = realpath($file->getPathname()); + } + } + return $results; + } + +} \ No newline at end of file diff --git a/lib/Frame.php b/lib/Frame.php deleted file mode 100755 index 13c7b0b..0000000 --- a/lib/Frame.php +++ /dev/null @@ -1,42 +0,0 @@ -handler = $handler; - $this->block = $block; - if (is_null($handler) && is_null($block)) { - throw new \LogicException("Both handler and block cannot be null, one must be non-null"); - } - $this->parent = $parent; - $this->scope = $scope; - } - - public function hasHandler(): bool { - return !is_null($this->handler); - } -} \ No newline at end of file diff --git a/lib/Func.php b/lib/Func.php deleted file mode 100755 index d897ec7..0000000 --- a/lib/Func.php +++ /dev/null @@ -1,28 +0,0 @@ -name = $name; - } - - public function getName(): string { - return $this->name; - } - - abstract public function getFrame(Context $context, ?Frame $frame = null): Frame; - -} diff --git a/lib/Func/Internal.php b/lib/Func/Internal.php deleted file mode 100755 index c5e57a8..0000000 --- a/lib/Func/Internal.php +++ /dev/null @@ -1,35 +0,0 @@ -callback = $cb; - $this->result = $result; - } - - public function getFrame(Context $context, ?Frame $frame = null): Frame { - return new Frame($this, null, null); - } - - public function execute(Frame $frame): void { - // TODO: handle argument passing - assert(empty($frame->calledArgs)); - ($this->callback)(); - } - -} \ No newline at end of file diff --git a/lib/Func/PHP.php b/lib/Func/PHP.php deleted file mode 100755 index c4e4b19..0000000 --- a/lib/Func/PHP.php +++ /dev/null @@ -1,30 +0,0 @@ -block = $block; - } - - public function getFrame(Context $context, ?Frame $frame = null): Frame { - return $this->block->getFrame($context, $frame); - } - -} diff --git a/lib/JIT/Helper.php b/lib/Helper.php similarity index 99% rename from lib/JIT/Helper.php rename to lib/Helper.php index d0fb7b2..6ac1e9d 100644 --- a/lib/JIT/Helper.php +++ b/lib/Helper.php @@ -1,7 +1,7 @@ context = $context; - } - - public function compile(Block $block): PHPLLVM\Value { - $return = $this->compileBlock($block); - $this->runQueue(); - return $return; - } - - public function compileFunc(CoreFunc $func): void { - if ($func instanceof CoreFunc\PHP) { - $this->compileBlock($func->block, $func->getName()); - $this->runQueue(); - return; - } elseif ($func instanceof CoreFunc\JIT) { - // No need to do anything, already compiled - return; - } elseif ($func instanceof CoreFunc\Internal) { - $this->context->functionProxies[strtolower($func->getName())] = $func; - return; - } - throw new \LogicException("Unknown func type encountered: " . get_class($func)); - } - - private function runQueue(): void { - while (!empty($this->queue)) { - $run = array_shift($this->queue); - $this->compileBlockInternal($run[0], $run[1], ...$run[2]); - } - } - - private function compileBlock(Block $block, ?string $funcName = null): PHPLLVM\Value { - if (!is_null($funcName)) { - $internalName = $funcName; - } else { - $internalName = "internal_" . (++self::$functionNumber); - } - $args = []; - $rawTypes = []; - $argVars = []; - if (!is_null($block->func)) { - $callbackType = ''; - if ($block->func->returnType instanceof Op\Type\Literal) { - switch ($block->func->returnType->name) { - case 'void': - $callbackType = 'void'; - break; - case 'int': - $callbackType = 'long long'; - break; - case 'string': - $callbackType = '__string__*'; - break; - default: - throw new \LogicException("Non-void return types not supported yet"); - } - } else { - $callbackType = '__value__'; - } - $returnType = $this->context->getTypeFromString($callbackType); - $this->context->functionReturnType[strtolower($internalName)] = $callbackType; - - $callbackType .= '(*)('; - $callbackSep = ''; - foreach ($block->func->params as $idx => $param) { - if (empty($param->result->usages)) { - // only compile for param - assert($param->declaredType instanceof Op\Type\Literal); - $rawType = Type::fromDecl($param->declaredType->name); - } else { - $rawType = $param->result->type; - } - $type = $this->context->getTypeFromType($rawType); - $callbackType .= $callbackSep . $this->context->getStringFromType($type); - $callbackSep = ', '; - $rawTypes[] = $rawType; - $args[] = $type; - } - $callbackType .= ')'; - } else { - $callbackType = 'void(*)()'; - $returnType = $this->context->getTypeFromString('void'); - } - - $isVarArgs = false; - - $func = $this->context->module->addFunction( - $internalName, - $this->context->context->functionType( - $returnType, - $isVarArgs, - ...$args - ) - ); - - foreach ($args as $idx => $arg) { - $argVars[] = new Variable($this->context, Variable::getTypeFromType($rawTypes[$idx]), Variable::KIND_VALUE, $func->getParam($idx)); - } - - if (!is_null($funcName)) { - $lcname = strtolower($funcName); - $this->context->functions[$lcname] = $func; - if ($isVarArgs) { - $this->context->functionProxies[$lcname] = new JIT\Call\Vararg($func, $funcName, count($args)); - } else { - $this->context->functionProxies[$lcname] = new JIT\Call\Native($func, $funcName, $args); - } - } - - $this->queue[] = [$func, $block, $argVars]; - if ($callbackType === 'void(*)()') { - $this->context->addExport($internalName, $callbackType, $block); - } - return $func; - } - - private function compileBlockInternal( - PHPLLVM\Value $func, - Block $block, - Variable ...$args - ): PHPLLVM\BasicBlock { - if ($this->context->scope->blockStorage->contains($block)) { - return $this->context->scope->blockStorage[$block]; - } - self::$blockNumber++; - $origBasicBlock = $basicBlock = $func->appendBasicBlock('block_' . self::$blockNumber); - $this->context->scope->blockStorage[$block] = $basicBlock; - $builder = $this->context->builder; - $builder->positionAtEnd($basicBlock); - // Handle hoisted variables - foreach ($block->orig->hoistedOperands as $operand) { - $this->context->makeVariableFromOp($func, $basicBlock, $block, $operand); - } - - for ($i = 0, $length = count($block->opCodes); $i < $length; $i++) { - $op = $block->opCodes[$i]; - switch ($op->type) { - case OpCode::TYPE_ARG_RECV: - $this->assignOperand($block->getOperand($op->arg1), $args[$op->arg2]); - break; - case OpCode::TYPE_ASSIGN: - $value = $this->context->getVariableFromOp($block->getOperand($op->arg3)); - $this->assignOperand($block->getOperand($op->arg2), $value); - $this->assignOperand($block->getOperand($op->arg1), $value); - break; - // case OpCode::TYPE_ARRAY_DIM_FETCH: - // $value = $this->context->getVariableFromOp($block->getOperand($op->arg2)); - // $dimOp = $block->getOperand($op->arg3); - // $dim = $this->context->getVariableFromOp($dimOp); - // if ($value->type & Variable::IS_NATIVE_ARRAY && $this->context->analyzer->needsBoundsCheck($value, $dimOp)) { - // // compile bounds check - // $builder->call( - // $this->context->lookupFunction('__nativearray__boundscheck'), - // $dim->value, - // $this->context->constantFromInteger($value->nextFreeElement) - // ); - // } - // $this->assignOperand( - // $block->getOperand($op->arg1), - // $value->dimFetch($dim) - // ); - // break; - // case OpCode::TYPE_INIT_ARRAY: - // case OpCode::TYPE_ADD_ARRAY_ELEMENT: - // $result = $this->context->getVariableFromOp($block->getOperand($op->arg1)); - // if ($result->type & Variable::IS_NATIVE_ARRAY) { - // if (is_null($op->arg3)) { - // $idx = $result->nextFreeElement; - // } else { - // // this is safe, since we only compile to native array if it's checked to be good - // $idx = $block->getOperand($op->arg3)->value; - // } - // $this->context->helper->assign( - // $gccBlock, - // \gcc_jit_context_new_array_access( - // $this->context->context, - // $this->context->location(), - // $result->rvalue, - // $this->context->constantFromInteger($idx, 'size_t') - // ), - // $this->context->getVariableFromOp($block->getOperand($op->arg2))->rvalue - // ); - // $result->nextFreeElement = max($result->nextFreeElement, $idx + 1); - // } else { - // throw new \LogicException('Hash tables not implemented yet'); - // } - // break; - case OpCode::TYPE_BOOLEAN_NOT: - $from = $this->context->getVariableFromOp($block->getOperand($op->arg2)); - if ($from->type === Variable::TYPE_NATIVE_BOOL) { - $value = $this->context->helper->loadValue($from); - } else { - $value = $this->context->castToBool($this->context->helper->loadValue($from)); - } - $__right = $value->typeOf()->constInt(1, false); - - - - - - - - $result = $this->context->builder->bitwiseXor($value, $__right); - - - $this->assignOperandValue($block->getOperand($op->arg1), $result); - break; - case OpCode::TYPE_CONCAT: - if (!$this->context->hasVariableOp($block->getOperand($op->arg1))) { - // don't bother with constant operations - break; - } - $result = $this->context->getVariableFromOp($block->getOperand($op->arg1)); - $left = $this->context->getVariableFromOp($block->getOperand($op->arg2)); - $right = $this->context->getVariableFromOp($block->getOperand($op->arg3)); - $this->context->type->string->concat($result, $left, $right); - break; - case OpCode::TYPE_CONST_FETCH: - $value = null; - if (!is_null($op->arg3)) { - // try NS constant fetch - $value = $this->context->constantFetch($block->getOperand($op->arg3)); - } - if (is_null($value)) { - $value = $this->context->constantFetch($block->getOperand($op->arg2)); - } - if (is_null($value)) { - throw new \RuntimeException('Unknown constant fetch'); - } - $this->assignOperand($block->getOperand($op->arg1), $value); - break; - case OpCode::TYPE_CAST_BOOL: - $value = $this->context->getVariableFromOp($block->getOperand($op->arg2)); - $this->assignOperand($block->getOperand($op->arg1), $value->castTo(Variable::TYPE_NATIVE_BOOL)); - break; - case OpCode::TYPE_ECHO: - case OpCode::TYPE_PRINT: - $argOffset = $op->type === OpCode::TYPE_ECHO ? $op->arg1 : $op->arg2; - $arg = $this->context->getVariableFromOp($block->getOperand($argOffset)); - $argValue = $this->context->helper->loadValue($arg); - switch ($arg->type) { - case Variable::TYPE_VALUE: - $argValue = $this->context->builder->call( - $this->context->lookupFunction('__value__readString') , - $argValue - - ); - - // Fall through intentional - case Variable::TYPE_STRING: - $fmt = $this->context->builder->pointerCast( - $this->context->constantFromString("%.*s"), - $this->context->getTypeFromString('char*') - ); - $offset = $this->context->structFieldMap[$argValue->typeOf()->getElementType()->getName()]['length']; - $__str__length = $this->context->builder->load( - $this->context->builder->structGep($argValue, $offset) - ); - $offset = $this->context->structFieldMap[$argValue->typeOf()->getElementType()->getName()]['value']; - $__str__value = $this->context->builder->structGep($argValue, $offset); - $this->context->builder->call( - $this->context->lookupFunction('printf') , - $fmt - , $__str__length - , $__str__value - - ); - - break; - case Variable::TYPE_NATIVE_LONG: - $fmt = $this->context->builder->pointerCast( - $this->context->constantFromString("%lld"), - $this->context->getTypeFromString('char*') - ); - $this->context->builder->call( - $this->context->lookupFunction('printf') , - $fmt - , $argValue - - ); - - break; - case Variable::TYPE_NATIVE_DOUBLE: - $fmt = $this->context->builder->pointerCast( - $this->context->constantFromString("%G"), - $this->context->getTypeFromString('char*') - ); - $this->context->builder->call( - $this->context->lookupFunction('printf') , - $fmt - , $argValue - - ); - - break; - case Variable::TYPE_NATIVE_BOOL: - $bool = $this->context->castToBool($argValue); - $prev = $this->context->builder->getInsertBlock(); - $ifBlock = $prev->insertBasicBlock('ifBlock'); - $prev->moveBefore($ifBlock); - - $endBlock[] = $tmp = $ifBlock->insertBasicBlock('endBlock'); - $this->context->builder->branchIf($bool, $ifBlock, $tmp); - - $this->context->builder->positionAtEnd($ifBlock); - { $fmt = $this->context->builder->pointerCast( - $this->context->constantFromString("1"), - $this->context->getTypeFromString('char*') - ); - $this->context->builder->call( - $this->context->lookupFunction('printf') , - $fmt - - ); - } - if ($this->context->builder->getInsertBlock()->getTerminator() === null) { - $this->context->builder->branch(end($endBlock)); - } - - $this->context->builder->positionAtEnd(array_pop($endBlock)); - - break; - - default: - throw new \LogicException("Echo for type $arg->type not implemented"); - } - if ($op->type === OpCode::TYPE_PRINT) { - $this->assignOperand( - $block->getOperand($op->arg1), - new Variable($this->context, Variable::TYPE_NATIVE_LONG, Variable::KIND_VALUE, $this->context->constantFromInteger(1)) - ); - } - break; - case OpCode::TYPE_MUL: - case OpCode::TYPE_PLUS: - case OpCode::TYPE_MINUS: - case OpCode::TYPE_DIV: - case OpCode::TYPE_MODULO: - case OpCode::TYPE_BITWISE_AND: - case OpCode::TYPE_BITWISE_OR: - case OpCode::TYPE_BITWISE_XOR: - case OpCode::TYPE_GREATER_OR_EQUAL: - case OpCode::TYPE_SMALLER_OR_EQUAL: - case OpCode::TYPE_GREATER: - case OpCode::TYPE_SMALLER: - case OpCode::TYPE_IDENTICAL: - case OpCode::TYPE_EQUAL: - $this->assignOperand( - $block->getOperand($op->arg1), - $this->context->helper->binaryOp( - $op, - $this->context->getVariableFromOp($block->getOperand($op->arg2)), - $this->context->getVariableFromOp($block->getOperand($op->arg3)) - ) - ); - break; - case OpCode::TYPE_UNARY_MINUS: - $this->assignOperand( - $block->getOperand($op->arg1), - $this->context->helper->unaryOp( - $op, - $this->context->getVariableFromOp($block->getOperand($op->arg2)), - ) - ); - break; - // case OpCode::TYPE_CASE: - case OpCode::TYPE_JUMP: - $newBlock = $this->compileBlockInternal($func, $op->block1, ...$args); - $builder->positionAtEnd($basicBlock); - $this->context->freeDeadVariables($func, $basicBlock, $block); - $builder->branch($newBlock); - return $origBasicBlock; - case OpCode::TYPE_JUMPIF: - $if = $this->compileBlockInternal($func, $op->block1, ...$args); - $else = $this->compileBlockInternal($func, $op->block2, ...$args); - - $builder->positionAtEnd($basicBlock); - - $condition = $this->context->castToBool( - $this->context->helper->loadValue($this->context->getVariableFromOp($block->getOperand($op->arg1))) - ); - - $this->context->freeDeadVariables($func, $basicBlock, $block); - $builder->branchIf($condition, $if, $else); - return $origBasicBlock; - case OpCode::TYPE_RETURN_VOID: - $this->context->freeDeadVariables($func, $basicBlock, $block); - $this->context->builder->returnVoid(); - - return $origBasicBlock; - case OpCode::TYPE_RETURN: - $return = $this->context->getVariableFromOp($block->getOperand($op->arg1)); - $return->addref(); - $retval = $this->context->helper->loadValue($return); - $this->context->freeDeadVariables($func, $basicBlock, $block); - $this->context->builder->returnValue($retval); - - return $origBasicBlock; - case OpCode::TYPE_FUNCDEF: - $nameOp = $block->getOperand($op->arg1); - assert($nameOp instanceof Operand\Literal); - $this->compileBlock($op->block1, $nameOp->value); - break; - case OpCode::TYPE_FUNCCALL_INIT: - $nameOp = $block->getOperand($op->arg1); - if (!$nameOp instanceof Operand\Literal) { - throw new \LogicException("Variable function calls not yet supported"); - } - $lcname = strtolower($nameOp->value); - if (isset($this->context->functionProxies[$lcname])) { - $this->context->scope->toCall = $this->context->functionProxies[$lcname]; - } else { - throw new \RuntimeException("Call to undefined function $lcname"); - } - $this->context->scope->args = []; - break; - case OpCode::TYPE_ARG_SEND: - $this->context->scope->args[] = $this->context->getVariableFromOp($block->getOperand($op->arg1)); - break; - case OpCode::TYPE_FUNCCALL_EXEC_NORETURN: - if (is_null($this->context->scope->toCall)) { - // short circuit - break; - } - $this->context->scope->toCall->call($this->context, ...$this->context->scope->args); - break; - case OpCode::TYPE_FUNCCALL_EXEC_RETURN: - $result = $this->context->scope->toCall->call($this->context, ...$this->context->scope->args); - $this->assignOperandValue($block->getOperand($op->arg1), $result); - break; - // case OpCode::TYPE_DECLARE_CLASS: - // $this->context->pushScope(); - // $this->context->scope->classId = $this->context->type->object->declareClass($block->getOperand($op->arg1)); - // $this->compileClass($op->block1, $this->context->scope->classId); - // $this->context->popScope(); - // break; - // case OpCode::TYPE_NEW: - // $class = $this->context->type->object->lookupOperand($block->getOperand($op->arg2)); - // $this->context->helper->assign( - // $gccBlock, - // $this->context->getVariableFromOp($block->getOperand($op->arg1))->lvalue, - // $this->context->type->object->allocate($class) - // ); - // $this->context->scope->toCall = null; - // $this->context->scope->args = []; - // break; - // case OpCode::TYPE_PROPERTY_FETCH: - // $result = $block->getOperand($op->arg1); - // $obj = $block->getOperand($op->arg2); - // $name = $block->getOperand($op->arg3); - // assert($name instanceof Operand\Literal); - // assert($obj->type->type === Type::TYPE_OBJECT); - // $this->context->scope->variables[$result] = $this->context->type->object->propertyFetch( - // $this->context->getVariableFromOp($obj)->rvalue, - // $obj->type->userType, - // $name->value - // ); - // break; - default: - throw new \LogicException("Unknown JIT opcode: ". $op->getType()); - } - } - throw new \LogicException("Reached the end of the loop, this shouldn't happen..."); - } - - private function compileClass(?Block $block, int $classId) { - if ($block === null) { - return; - } - foreach ($block->opCodes as $op) { - switch ($op->type) { - case OpCode::TYPE_DECLARE_PROPERTY: - $name = $block->getOperand($op->arg1); - assert($name instanceof Operand\Literal); - assert(is_null($op->arg2)); // no defaults for now - $type = Variable::getTypeFromType($block->getOperand($op->arg3)->type); - $this->context->type->object->defineProperty($classId, $name->value, $type); - break; - default: - var_dump($op); - throw new \LogicException('Other class body types are not jittable for now'); - } - - } - } - - private function assignOperand(Operand $result, Variable $value): void { - if (empty($result->usages) && !$this->context->scope->variables->contains($result)) { - return; - } - if (!$this->context->hasVariableOp($result)) { - // it's a kind! - $this->context->makeVariableFromValueOp($this->context->helper->loadValue($value), $result); - return; - } - $result = $this->context->getVariableFromOp($result); - if ($result->kind !== Variable::KIND_VARIABLE) { - throw new \LogicException("Cannot assign to a value"); - } - if ($value->type === $result->type) { - $result->free(); - if ($value->type & Variable::IS_NATIVE_ARRAY) { - // copy over the nextfreelement - //$result->nextFreeElement = $value->nextFreeElement; - } - $this->context->builder->store( - $this->context->helper->loadValue($value), - $result->value - ); - $result->addref(); - return; - } elseif ($result->type === Variable::TYPE_VALUE) { - // wrap - $valueRef = $result->value; - $valueFrom = $value->value; - switch ($value->type) { - case Variable::TYPE_NULL: - $this->context->builder->call( - $this->context->lookupFunction('__value__writeNull') , - $valueRef - - ); - - return; - case Variable::TYPE_NATIVE_LONG: - $this->context->builder->call( - $this->context->lookupFunction('__value__writeLong') , - $valueRef - , $valueFrom - - ); - - return; - case Variable::TYPE_NATIVE_DOUBLE: - $this->context->builder->call( - $this->context->lookupFunction('__value__writeDouble') , - $valueRef - , $valueFrom - - ); - - return; - default: - throw new \LogicException("Source type: {$value->type}"); - } - } - throw new \LogicException("Cannot assign operands of different types (yet): {$value->type}, {$result->type}"); - } - - private function assignOperandValue(Operand $result, PHPLLVM\Value $value): void { - if (empty($result->usages) && !$this->context->scope->variables->contains($result)) { - return; - } - if (!$this->context->hasVariableOp($result)) { - // it's a kind! - $this->context->makeVariableFromValueOp($value, $result); - return; - } - $result = $this->context->getVariableFromOp($result); - if ($result->kind !== Variable::KIND_VARIABLE) { - throw new \LogicException("Cannot assign to a value"); - } - $result->free(); - - $this->context->builder->store( - $value, - $result->value - ); - $result->addref(); - } - -} diff --git a/lib/JIT/Builtin/MemoryManager.php b/lib/JIT/Builtin/MemoryManager.php deleted file mode 100644 index 7f93f14..0000000 --- a/lib/JIT/Builtin/MemoryManager.php +++ /dev/null @@ -1,349 +0,0 @@ -context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('size_t') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('__mm__malloc', $fntype___cfcd208495d565ef66e7dff9f98764da); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(\PHPLLVM\Attribute::INDEX_FUNCTION, $this->context->attributes['alwaysinline']); - - - - $this->context->registerFunction('__mm__malloc', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('int8*') - , $this->context->getTypeFromString('size_t') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('__mm__realloc', $fntype___cfcd208495d565ef66e7dff9f98764da); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(\PHPLLVM\Attribute::INDEX_FUNCTION, $this->context->attributes['alwaysinline']); - - - - - $this->context->registerFunction('__mm__realloc', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('void'), - false , - $this->context->getTypeFromString('int8*') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('__mm__free', $fntype___cfcd208495d565ef66e7dff9f98764da); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(\PHPLLVM\Attribute::INDEX_FUNCTION, $this->context->attributes['alwaysinline']); - - - - $this->context->registerFunction('__mm__free', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - - } - - public function malloc(PHPLLVM\Type $type): PHPLLVM\Value { - if ($type instanceof \PHPLLVM\Type) { - $type = $type; - } elseif ($type instanceof \PHPLLVM\Value) { - $type = $type->typeOf(); - } else { - throw new \LogicException("Attempt to call sizeof on non-PHPLLVM type/value"); - } - $size = $this->context->builder->ptrToInt( - $this->context->builder->gep( - $type->pointerType(0)->constNull(), - $this->context->context->int32Type()->constInt(1, false) - ), - $this->context->getTypeFromString('size_t') - ); - $ptr = $this->context->builder->call( - $this->context->lookupFunction('__mm__malloc') , - $size - - ); - - return $this->context->builder->pointerCast($ptr, $type->pointerType(0)); - } - - public function mallocWithExtra(PHPLLVM\Type $type, PHPLLVM\Value $extra): PHPLLVM\Value { - if ($type instanceof \PHPLLVM\Type) { - $type = $type; - } elseif ($type instanceof \PHPLLVM\Value) { - $type = $type->typeOf(); - } else { - throw new \LogicException("Attempt to call sizeof on non-PHPLLVM type/value"); - } - $size = $this->context->builder->ptrToInt( - $this->context->builder->gep( - $type->pointerType(0)->constNull(), - $this->context->context->int32Type()->constInt(1, false) - ), - $this->context->getTypeFromString('size_t') - ); - $__right = $this->context->builder->intCast($extra, $size->typeOf()); - - - - - - - - - - - $size = $this->context->builder->addNoUnsignedWrap($size, $__right); - $ptr = $this->context->builder->call( - $this->context->lookupFunction('__mm__malloc') , - $size - - ); - - return $this->context->builder->pointerCast($ptr, $type->pointerType(0)); - } - - public function realloc(PHPLLVM\Value $value, PHPLLVM\Value $extra): PHPLLVM\Value { - $type = $value->typeOf()->getElementType(); - if ($type instanceof \PHPLLVM\Type) { - $type = $type; - } elseif ($type instanceof \PHPLLVM\Value) { - $type = $type->typeOf(); - } else { - throw new \LogicException("Attempt to call sizeof on non-PHPLLVM type/value"); - } - $size = $this->context->builder->ptrToInt( - $this->context->builder->gep( - $type->pointerType(0)->constNull(), - $this->context->context->int32Type()->constInt(1, false) - ), - $this->context->getTypeFromString('size_t') - ); - $__right = $this->context->builder->intCast($extra, $size->typeOf()); - - - - - - - - - - - $allocSize = $this->context->builder->addNoUnsignedWrap($size, $__right); - $__type = $this->context->getTypeFromString('int8*'); - - - $__kind = $__type->getKind(); - $__value = $value; - switch ($__kind) { - case \PHPLLVM\Type::KIND_INTEGER: - if (!is_object($__value)) { - $void = $__type->constInt($__value, false); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - if ($__other_type->getWidth() >= $__type->getWidth()) { - $void = $this->context->builder->truncOrBitCast($__value, $__type); - } else { - $void = $this->context->builder->zExtOrBitCast($__value, $__type); - } - break; - case \PHPLLVM\Type::KIND_DOUBLE: - $void = $this->context->builder->fpToUi($__value, $__type); - - - break; - case \PHPLLVM\Type::KIND_ARRAY: - case \PHPLLVM\Type::KIND_POINTER: - $void = $this->context->builder->ptrToInt($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (int, " . $__other_type->toString() . ")"); - } - break; - case \PHPLLVM\Type::KIND_DOUBLE: - if (!is_object($__value)) { - $void = $__type->constReal($value); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - $void = $this->context->builder->uiToFp($__value, $__type); - - - break; - case \PHPLLVM\Type::KIND_DOUBLE: - $void = $this->context->builder->fpCast($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (double, " . $__other_type->toString() . ")"); - } - break; - case \PHPLLVM\Type::KIND_ARRAY: - case \PHPLLVM\Type::KIND_POINTER: - if (!is_object($__value)) { - // this is very likely very wrong... - $void = $__type->constInt($__value, false); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - $void = $this->context->builder->intToPtr($__value, $__type); - break; - case \PHPLLVM\Type::KIND_ARRAY: - // $__tmp = $this->context->builder->($__value, $this->context->context->int64Type()); - // $(result) = $this->context->builder->intToPtr($__tmp, $__type); - // break; - case \PHPLLVM\Type::KIND_POINTER: - $void = $this->context->builder->pointerCast($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (double, " . $__other_type->toString() . ")"); - } - break; - default: - throw new \LogicException("Unsupported type cast: " . $__type->toString()); - } - $ptr = $this->context->builder->call( - $this->context->lookupFunction('__mm__realloc') , - $void - , $allocSize - - ); - - return $this->context->builder->pointerCast($ptr, $type->pointerType(0)); - } - - public function free(PHPLLVM\Value $value): void { - $__type = $this->context->getTypeFromString('int8*'); - - - $__kind = $__type->getKind(); - $__value = $value; - switch ($__kind) { - case \PHPLLVM\Type::KIND_INTEGER: - if (!is_object($__value)) { - $void = $__type->constInt($__value, false); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - if ($__other_type->getWidth() >= $__type->getWidth()) { - $void = $this->context->builder->truncOrBitCast($__value, $__type); - } else { - $void = $this->context->builder->zExtOrBitCast($__value, $__type); - } - break; - case \PHPLLVM\Type::KIND_DOUBLE: - - $void = $this->context->builder->fpToSi($__value, $__type); - - break; - case \PHPLLVM\Type::KIND_ARRAY: - case \PHPLLVM\Type::KIND_POINTER: - $void = $this->context->builder->ptrToInt($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (int, " . $__other_type->toString() . ")"); - } - break; - case \PHPLLVM\Type::KIND_DOUBLE: - if (!is_object($__value)) { - $void = $__type->constReal($value); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - - $void = $this->context->builder->siToFp($__value, $__type); - - break; - case \PHPLLVM\Type::KIND_DOUBLE: - $void = $this->context->builder->fpCast($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (double, " . $__other_type->toString() . ")"); - } - break; - case \PHPLLVM\Type::KIND_ARRAY: - case \PHPLLVM\Type::KIND_POINTER: - if (!is_object($__value)) { - // this is very likely very wrong... - $void = $__type->constInt($__value, false); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - $void = $this->context->builder->intToPtr($__value, $__type); - break; - case \PHPLLVM\Type::KIND_ARRAY: - // $__tmp = $this->context->builder->($__value, $this->context->context->int64Type()); - // $(result) = $this->context->builder->intToPtr($__tmp, $__type); - // break; - case \PHPLLVM\Type::KIND_POINTER: - $void = $this->context->builder->pointerCast($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (double, " . $__other_type->toString() . ")"); - } - break; - default: - throw new \LogicException("Unsupported type cast: " . $__type->toString()); - } - $this->context->builder->call( - $this->context->lookupFunction('__mm__free') , - $void - - ); - - } - -} diff --git a/lib/JIT/Builtin/MemoryManager/Native.php b/lib/JIT/Builtin/MemoryManager/Native.php deleted file mode 100644 index 79eccdf..0000000 --- a/lib/JIT/Builtin/MemoryManager/Native.php +++ /dev/null @@ -1,119 +0,0 @@ -context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('size_t') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('malloc', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - - $this->context->registerFunction('malloc', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('int8*') - , $this->context->getTypeFromString('size_t') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('realloc', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - - - $this->context->registerFunction('realloc', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('void'), - false , - $this->context->getTypeFromString('int8*') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('free', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - - $this->context->registerFunction('free', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - - } - - public function implement(): void { - // Todo - $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->lookupFunction('__mm__malloc'); - $block___c4ca4238a0b923820dcc509a6f75849b = $fn___c4ca4238a0b923820dcc509a6f75849b->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___c4ca4238a0b923820dcc509a6f75849b); - $size = $fn___c4ca4238a0b923820dcc509a6f75849b->getParam(0); - - $result = $this->context->builder->call( - $this->context->lookupFunction('malloc') , - $size - - ); - $this->context->builder->returnValue($result); - - $this->context->builder->clearInsertionPosition(); - $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $this->context->lookupFunction('__mm__realloc'); - $block___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___eccbc87e4b5ce2fe28308fd9f2a7baf3); - $void = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->getParam(0); - $size = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->getParam(1); - - $result = $this->context->builder->call( - $this->context->lookupFunction('realloc') , - $void - , $size - - ); - $this->context->builder->returnValue($result); - - $this->context->builder->clearInsertionPosition(); - $fn___e4da3b7fbbce2345d7772b0674a318d5 = $this->context->lookupFunction('__mm__free'); - $block___e4da3b7fbbce2345d7772b0674a318d5 = $fn___e4da3b7fbbce2345d7772b0674a318d5->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___e4da3b7fbbce2345d7772b0674a318d5); - $void = $fn___e4da3b7fbbce2345d7772b0674a318d5->getParam(0); - - $this->context->builder->call( - $this->context->lookupFunction('free') , - $void - - ); - $this->context->builder->returnVoid(); - - $this->context->builder->clearInsertionPosition(); - } - -} diff --git a/lib/JIT/Builtin/MemoryManager/PHP.php b/lib/JIT/Builtin/MemoryManager/PHP.php deleted file mode 100755 index 2b50430..0000000 --- a/lib/JIT/Builtin/MemoryManager/PHP.php +++ /dev/null @@ -1,347 +0,0 @@ -context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('size_t') - , $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('uint32') - , $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('uint32') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('_emalloc', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['nocapture'], 0); - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(3 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(3 + 1, $this->context->attributes['nocapture'], 0); - - - - $this->context->registerFunction('_emalloc', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('int8*') - , $this->context->getTypeFromString('size_t') - , $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('uint32') - , $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('uint32') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('_erealloc', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(2 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(2 + 1, $this->context->attributes['nocapture'], 0); - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(4 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(4 + 1, $this->context->attributes['nocapture'], 0); - - - - $this->context->registerFunction('_erealloc', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('void'), - false , - $this->context->getTypeFromString('int8*') - , $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('uint32') - , $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('uint32') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('_efree', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['nocapture'], 0); - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(3 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(3 + 1, $this->context->attributes['nocapture'], 0); - - - - $this->context->registerFunction('_efree', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - - } else { - $fntype___c4ca4238a0b923820dcc509a6f75849b = $this->context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('size_t') - - ); - $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->module->addFunction('_emalloc', $fntype___c4ca4238a0b923820dcc509a6f75849b); - - - - $this->context->registerFunction('_emalloc', $fn___c4ca4238a0b923820dcc509a6f75849b); - - - - - - $fntype___c4ca4238a0b923820dcc509a6f75849b = $this->context->context->functionType( - $this->context->getTypeFromString('int8*'), - false , - $this->context->getTypeFromString('int8*') - , $this->context->getTypeFromString('size_t') - - ); - $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->module->addFunction('_erealloc', $fntype___c4ca4238a0b923820dcc509a6f75849b); - - - - - $this->context->registerFunction('_erealloc', $fn___c4ca4238a0b923820dcc509a6f75849b); - - - - - - $fntype___c4ca4238a0b923820dcc509a6f75849b = $this->context->context->functionType( - $this->context->getTypeFromString('void'), - false , - $this->context->getTypeFromString('int8*') - - ); - $fn___c4ca4238a0b923820dcc509a6f75849b = $this->context->module->addFunction('_efree', $fntype___c4ca4238a0b923820dcc509a6f75849b); - - - - $this->context->registerFunction('_efree', $fn___c4ca4238a0b923820dcc509a6f75849b); - - - - - - - } - } - - public function implement(): void { - if (\PHP_DEBUG) { - // FIXME: Use real values here, not constants. - - // These variables are a hack because compile{} - // blocks currently only accept variables as arguments. - $jit = $this->context->constantFromString("jit"); - $__type = $this->context->getTypeFromString('int32'); - - - $__kind = $__type->getKind(); - $__value = 2; - switch ($__kind) { - case \PHPLLVM\Type::KIND_INTEGER: - if (!is_object($__value)) { - $two = $__type->constInt($__value, false); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - if ($__other_type->getWidth() >= $__type->getWidth()) { - $two = $this->context->builder->truncOrBitCast($__value, $__type); - } else { - $two = $this->context->builder->zExtOrBitCast($__value, $__type); - } - break; - case \PHPLLVM\Type::KIND_DOUBLE: - - $two = $this->context->builder->fpToSi($__value, $__type); - - break; - case \PHPLLVM\Type::KIND_ARRAY: - case \PHPLLVM\Type::KIND_POINTER: - $two = $this->context->builder->ptrToInt($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (int, " . $__other_type->toString() . ")"); - } - break; - case \PHPLLVM\Type::KIND_DOUBLE: - if (!is_object($__value)) { - $two = $__type->constReal(2); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - - $two = $this->context->builder->siToFp($__value, $__type); - - break; - case \PHPLLVM\Type::KIND_DOUBLE: - $two = $this->context->builder->fpCast($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (double, " . $__other_type->toString() . ")"); - } - break; - case \PHPLLVM\Type::KIND_ARRAY: - case \PHPLLVM\Type::KIND_POINTER: - if (!is_object($__value)) { - // this is very likely very wrong... - $two = $__type->constInt($__value, false); - break; - } - $__other_type = $__value->typeOf(); - switch ($__other_type->getKind()) { - case \PHPLLVM\Type::KIND_INTEGER: - $two = $this->context->builder->intToPtr($__value, $__type); - break; - case \PHPLLVM\Type::KIND_ARRAY: - // $__tmp = $this->context->builder->($__value, $this->context->context->int64Type()); - // $(result) = $this->context->builder->intToPtr($__tmp, $__type); - // break; - case \PHPLLVM\Type::KIND_POINTER: - $two = $this->context->builder->pointerCast($__value, $__type); - break; - default: - throw new \LogicException("Unknown how to handle type pair (double, " . $__other_type->toString() . ")"); - } - break; - default: - throw new \LogicException("Unsupported type cast: " . $__type->toString()); - } - - - $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $this->context->lookupFunction('__mm__malloc'); - $block___eccbc87e4b5ce2fe28308fd9f2a7baf3 = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___eccbc87e4b5ce2fe28308fd9f2a7baf3); - $size = $fn___eccbc87e4b5ce2fe28308fd9f2a7baf3->getParam(0); - - $result = $this->context->builder->call( - $this->context->lookupFunction('_emalloc') , - $size - , $jit - , $two - , $jit - , $two - - ); - $this->context->builder->returnValue($result); - - $this->context->builder->clearInsertionPosition(); - $fn___e4da3b7fbbce2345d7772b0674a318d5 = $this->context->lookupFunction('__mm__realloc'); - $block___e4da3b7fbbce2345d7772b0674a318d5 = $fn___e4da3b7fbbce2345d7772b0674a318d5->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___e4da3b7fbbce2345d7772b0674a318d5); - $void = $fn___e4da3b7fbbce2345d7772b0674a318d5->getParam(0); - $size = $fn___e4da3b7fbbce2345d7772b0674a318d5->getParam(1); - - $result = $this->context->builder->call( - $this->context->lookupFunction('_erealloc') , - $void - , $size - , $jit - , $two - , $jit - , $two - - ); - $this->context->builder->returnValue($result); - - $this->context->builder->clearInsertionPosition(); - $fn___8f14e45fceea167a5a36dedd4bea2543 = $this->context->lookupFunction('__mm__free'); - $block___8f14e45fceea167a5a36dedd4bea2543 = $fn___8f14e45fceea167a5a36dedd4bea2543->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___8f14e45fceea167a5a36dedd4bea2543); - $void = $fn___8f14e45fceea167a5a36dedd4bea2543->getParam(0); - - $this->context->builder->call( - $this->context->lookupFunction('_efree') , - $void - , $jit - , $two - , $jit - , $two - - ); - $this->context->builder->returnVoid(); - - $this->context->builder->clearInsertionPosition(); - } else { - $fn___45c48cce2e2d7fbdea1afc51c7c6ad26 = $this->context->lookupFunction('__mm__malloc'); - $block___45c48cce2e2d7fbdea1afc51c7c6ad26 = $fn___45c48cce2e2d7fbdea1afc51c7c6ad26->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___45c48cce2e2d7fbdea1afc51c7c6ad26); - $size = $fn___45c48cce2e2d7fbdea1afc51c7c6ad26->getParam(0); - - $result = $this->context->builder->call( - $this->context->lookupFunction('_emalloc') , - $size - - ); - $this->context->builder->returnValue($result); - - $this->context->builder->clearInsertionPosition(); - $fn___6512bd43d9caa6e02c990b0a82652dca = $this->context->lookupFunction('__mm__realloc'); - $block___6512bd43d9caa6e02c990b0a82652dca = $fn___6512bd43d9caa6e02c990b0a82652dca->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___6512bd43d9caa6e02c990b0a82652dca); - $void = $fn___6512bd43d9caa6e02c990b0a82652dca->getParam(0); - $size = $fn___6512bd43d9caa6e02c990b0a82652dca->getParam(1); - - $result = $this->context->builder->call( - $this->context->lookupFunction('_erealloc') , - $void - , $size - - ); - $this->context->builder->returnValue($result); - - $this->context->builder->clearInsertionPosition(); - $fn___c51ce410c124a10e0db5e4b97fc2af39 = $this->context->lookupFunction('__mm__free'); - $block___c51ce410c124a10e0db5e4b97fc2af39 = $fn___c51ce410c124a10e0db5e4b97fc2af39->appendBasicBlock('main'); - $this->context->builder->positionAtEnd($block___c51ce410c124a10e0db5e4b97fc2af39); - $void = $fn___c51ce410c124a10e0db5e4b97fc2af39->getParam(0); - - $this->context->builder->call( - $this->context->lookupFunction('_efree') , - $void - - ); - $this->context->builder->returnVoid(); - - $this->context->builder->clearInsertionPosition(); - } - } -} diff --git a/lib/JIT/Builtin/Output.php b/lib/JIT/Builtin/Output.php deleted file mode 100644 index d7a74a7..0000000 --- a/lib/JIT/Builtin/Output.php +++ /dev/null @@ -1,83 +0,0 @@ -context->context->functionType( - $this->context->getTypeFromString('int32'), - true , - $this->context->getTypeFromString('char*') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('printf', $fntype___cfcd208495d565ef66e7dff9f98764da); - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(0 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(0 + 1, $this->context->attributes['nocapture'], 0); - - - $this->context->registerFunction('printf', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('int32'), - true , - $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('char*') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('sprintf', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(1 + 1, $this->context->attributes['nocapture'], 0); - - - $this->context->registerFunction('sprintf', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - - $fntype___cfcd208495d565ef66e7dff9f98764da = $this->context->context->functionType( - $this->context->getTypeFromString('int32'), - true , - $this->context->getTypeFromString('char*') - , $this->context->getTypeFromString('size_t') - , $this->context->getTypeFromString('char*') - - ); - $fn___cfcd208495d565ef66e7dff9f98764da = $this->context->module->addFunction('snprintf', $fntype___cfcd208495d565ef66e7dff9f98764da); - - - - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(2 + 1, $this->context->attributes['readonly'], 0); - $fn___cfcd208495d565ef66e7dff9f98764da->addAttributeAtIndex(2 + 1, $this->context->attributes['nocapture'], 0); - - - $this->context->registerFunction('snprintf', $fn___cfcd208495d565ef66e7dff9f98764da); - - - - - } - -} \ No newline at end of file diff --git a/lib/JIT/Scope.php b/lib/JIT/Scope.php deleted file mode 100755 index d678aa9..0000000 --- a/lib/JIT/Scope.php +++ /dev/null @@ -1,23 +0,0 @@ -blockStorage = new \SplObjectStorage; - $this->variables = new \SplObjectStorage; - } -} diff --git a/lib/Module.php b/lib/Module.php index e6cc945..add532b 100755 --- a/lib/Module.php +++ b/lib/Module.php @@ -19,9 +19,5 @@ public function getFunctions(): array; public function init(Runtime $runtime): void; - public function jitInit(JIT\Context $context): void; - - public function jitShutdown(JIT\Context $context): void; - public function shutdown(): void; } diff --git a/lib/ModuleAbstract.php b/lib/ModuleAbstract.php index 5da21e0..5a3fbf2 100755 --- a/lib/ModuleAbstract.php +++ b/lib/ModuleAbstract.php @@ -30,14 +30,6 @@ public function shutdown(): void { } - public function jitInit(JIT\Context $context): void { - - } - - public function jitShutdown(JIT\Context $context): void { - - } - protected function parseAndCompileFunction(string $name, string $filename): Func { $script = $this->runtime->parse(file_get_contents($filename), $filename); $func = $this->findFunction($name, $script); diff --git a/lib/OpCode.php b/lib/OpCode.php deleted file mode 100755 index cbbb8ab..0000000 --- a/lib/OpCode.php +++ /dev/null @@ -1,92 +0,0 @@ -type = $type; - $this->arg1 = $arg1; - $this->arg2 = $arg2; - $this->arg3 = $arg3; - } - - public function getType(): string { - $r = new \ReflectionClass(__CLASS__); - foreach ($r->getConstants() as $name => $value) { - if ($value === $this->type) { - return $name; - } - } - return 'unknown opcode'; - } -} \ No newline at end of file diff --git a/lib/Printer.php b/lib/Printer.php deleted file mode 100755 index e1d0dd2..0000000 --- a/lib/Printer.php +++ /dev/null @@ -1,63 +0,0 @@ -seen = new \SplObjectStorage; - return $this->printBlock($block); - } - - private function printBlock(Block $block): string { - if ($this->seen->contains($block)) { - return ''; - } - $id = $this->seen->count(); - $this->seen[$block] = $id; - $return = "block_$id:\n"; - $append = ''; - foreach ($block->opCodes as $op) { - $return .= ' ' . $op->getType() . '('; - $return .= $this->renderArg($op->arg1, $block); - $return .= ', ' . $this->renderArg($op->arg2, $block); - $return .= ', ' . $this->renderArg($op->arg3, $block); - $return .= ')'; - if (!is_null($op->block1)) { - $append .= $this->printBlock($op->block1); - $return .= "\n goto block_" . $this->seen[$op->block1]; - } - if (!is_null($op->block2)) { - $append .= $this->printBlock($op->block2); - $return .= "\n goto block_" . $this->seen[$op->block2]; - } - $return .= "\n"; - } - return $return . "\n" . $append; - } - - private function renderArg(?int $arg, Block $block): string { - if (is_null($arg)) { - return 'null'; - } - $operand = $block->getOperand($arg); - if ($operand instanceof Operand\Literal) { - return 'LITERAL(' . var_export($operand->value, true) . ')'; - } - return '$' . $arg; - } - - -} \ No newline at end of file diff --git a/lib/JIT/Result.php b/lib/Result.php similarity index 76% rename from lib/JIT/Result.php rename to lib/Result.php index 273126e..8e878e8 100755 --- a/lib/JIT/Result.php +++ b/lib/Result.php @@ -7,7 +7,7 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCompiler\JIT; +namespace PHPCompiler; use PHPCompiler\Func; use PHPCompiler\Handler; @@ -37,22 +37,6 @@ public function __destruct() { } } - public function getFunc(string $publicName, string $funcName, string $callbackType): Func { - return new Func\JIT( - $publicName, - $this->getCallable($funcName, $callbackType), - $this - ); - } - - public function getHandler(string $funcName, string $callbackType): Handler { - return new Func\JIT( - $funcName, - $this->getCallable($funcName, $callbackType), - $this - ); - } - public function getCallable(string $funcName, string $callbackType): callable { $address = $this->engine->getFunctionAddress($funcName); $code = FFI::new('size_t'); diff --git a/lib/Runtime.php b/lib/Runtime.php index 4764796..901024f 100755 --- a/lib/Runtime.php +++ b/lib/Runtime.php @@ -20,29 +20,22 @@ use PhpParser\NodeVisitor; use PhpParser\ParserFactory; use PHPTypes\State; -use PHPCompiler\VM\Optimizer; -use PHPCompiler\VM\Context as VMContext; -use PHPCompiler\JIT\Context as JITContext; class Runtime { - const MODE_NORMAL = 0b0001; - const MODE_AOT = 0b0010; + const MODE_EXECUTABLE = 0b0001; + const MODE_SHARED_OBJECT = 0b0010; public Compiler $compiler; public Parser $parser; public Traverser $preprocessor; public Traverser $postprocessor; public LivenessDetector $detector; - public Optimizer $assignOpResolver; - public VMContext $vmContext; - public VM $vm; - private ?JITContext $jitContext = null; - private ?JIT $jit = null; + public Context $context; public array $modules = []; public int $mode; public ?string $debugFile = null; - public function __construct(int $mode = self::MODE_NORMAL) { + public function __construct(int $mode = self::MODE_EXECUTABLE) { $this->mode = $mode; $astTraverser = new NodeTraverser; $astTraverser->addVisitor( @@ -59,13 +52,9 @@ public function __construct(int $mode = self::MODE_NORMAL) { $this->postprocessor = new Traverser; $this->postprocessor->addVisitor(new Visitor\PhiResolver); $this->detector = new LivenessDetector; - $this->assignOpResolver = new Optimizer\AssignOp; $this->typeReconstructor = new TypeReconstructor; - $this->compiler = new Compiler; - $this->vmContext = new VMContext($this); - $this->vm = new VM($this->vmContext); $this->loadCoreModules(); } @@ -84,42 +73,9 @@ private function loadCoreModules(): void { $this->load(new ext\standard\Module); } - public function loadJit(): JIT { - if (is_null($this->jit)) { - $this->jit = new JIT( - $this->loadJitContext() - ); - foreach ($this->modules as $module) { - foreach ($module->getFunctions() as $func) { - $this->jit->compileFunc($func); - } - } - } - return $this->jit; - } - - public function loadJitContext(): JITContext { - if (is_null($this->jitContext)) { - $this->jitContext = new JITContext( - $this, - $this->mode === self::MODE_NORMAL ? JIT\Builtin::LOAD_TYPE_EMBED : JIT\Builtin::LOAD_TYPE_STANDALONE - ); - if (!is_null($this->debugFile)) { - $this->jitContext->setDebugFile($this->debugFile); - } - foreach ($this->modules as $module) { - $this->jitContext->registerModule($module); - } - } - return $this->jitContext; - } - public function load(Module $module): void { $this->modules[] = $module; $module->init($this); - foreach ($module->getFunctions() as $function) { - $this->vmContext->declareFunction($function); - } } public function parse(string $code, string $filename): Script { @@ -132,39 +88,9 @@ public function parse(string $code, string $filename): Script { return $script; } - public function compile(Script $script): ?Block { - $block = $this->compiler->compile($script); - $this->assignOpResolver->optimize($block); - return $block; - } - - public function compileFunc(string $name, CfgFunc $func): Func { - $compiled = $this->compiler->compileFunc($name, $func); - $this->assignOpResolver->optimize($compiled->block); - return $compiled; - } - - public function jit(?Block $block) { - $this->loadJit()->compile($block); - $this->loadJitContext()->compileInPlace(); - } - - public function standalone(?Block $block, string $outfile) { - $context = $this->loadJitContext(); - $context->setMain($this->loadJit()->compile($block)); - $context->compileToFile($outfile); - } - - public function parseAndCompile(string $code, string $filename): ?Block { - return $this->compile($this->parse($code, $filename)); - } - - public function parseAndCompileFile(string $filename): ?Block { - return $this->compile($this->parse(file_get_contents($filename), $filename)); - } - - public function run(?Block $block) { - return $this->vm->run($block); + public function compile(string $entrypoint, string $dir, FileResolver $resolver): void { + $files = $resolver->resolve($dir); + var_dump($files); } } diff --git a/lib/Handler.php b/lib/Scope.php similarity index 70% rename from lib/Handler.php rename to lib/Scope.php index b291370..fd4fbff 100755 --- a/lib/Handler.php +++ b/lib/Scope.php @@ -1,17 +1,14 @@ -context = $context; - } - - public function run(Block $block): int { - if (!is_null($block->handler)) { - $block->handler->execute($block->getFrame($this->context)); - return self::SUCCESS; - } - - $this->context->push($block->getFrame($this->context)); -nextframe: - $frame = $this->context->pop(); - - if (is_null($frame)) { - return self::SUCCESS; - } -restart: - - while ($frame->pos < $frame->block->nOpCodes) { - $op = $frame->block->opCodes[$frame->pos++]; - switch ($op->type) { - case OpCode::TYPE_TYPE_ASSERT: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg1->copyFrom($arg2); - break; - case OpCode::TYPE_ASSIGN: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg3 = $frame->scope[$op->arg3]; - $arg2->copyFrom($arg3); - $arg1->copyFrom($arg3); - break; - case OpCode::TYPE_ARRAY_DIM_FETCH: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - if (is_null($op->arg3)) { - if ($arg2->type !== Variable::TYPE_ARRAY) { - throw new \LogicException('[] is only supported for arrays'); - } - $arg1->indirect($arg2->toArray()->append(new Variable)); - break; - } - $arg3 = $frame->scope[$op->arg3]; - if ($arg2->type === Variable::TYPE_STRING) { - $arg1->string($arg2->toString()[$arg3->toInt()]); - } elseif ($arg2->type === Variable::TYPE_ARRAY) { - $arg1->indirect($arg2->toArray()->findVariable($arg3, false)); - } else { - throw new \LogicException('Illegal offset'); - } - break; - case OpCode::TYPE_CAST_BOOL: - $frame->scope[$op->arg1]->castFrom(Variable::TYPE_BOOLEAN, $frame->scope[$op->arg2]); - break; - case OpCode::TYPE_IDENTICAL: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg3 = $frame->scope[$op->arg3]; - $arg1->bool($arg2->identicalTo($arg3)); - break; - case OpCode::TYPE_EQUAL: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg3 = $frame->scope[$op->arg3]; - $arg1->bool($arg2->equals($arg3)); - break; - case OpCode::TYPE_SMALLER: - case OpCode::TYPE_GREATER: - case OpCode::TYPE_SMALLER_OR_EQUAL: - case OpCode::TYPE_GREATER_OR_EQUAL: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg3 = $frame->scope[$op->arg3]; - $arg1->compareOp($op->type, $arg2, $arg3); - break; - case OpCode::TYPE_PLUS: - case OpCode::TYPE_MINUS: - case OpCode::TYPE_MUL: - case OpCode::TYPE_DIV: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg3 = $frame->scope[$op->arg3]; - $arg1->numericOp($op->type, $arg2, $arg3); - break; - case OpCode::TYPE_BITWISE_AND: - case OpCode::TYPE_BITWISE_OR: - case OpCode::TYPE_BITWISE_XOR: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg3 = $frame->scope[$op->arg3]; - $arg1->bitwiseOp($op->type, $arg2, $arg3); - break; - - case OpCode::TYPE_UNARY_MINUS: - case OpCode::TYPE_UNARY_PLUS: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - $arg1->unaryOp($op->type, $arg2); - break; - case OpCode::TYPE_CONCAT: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]->toString(); - $arg3 = $frame->scope[$op->arg3]->toString(); - $arg1->string($arg2 . $arg3); - break; - case OpCode::TYPE_ECHO: - echo $frame->scope[$op->arg1]->toString(); - break; - case OpCode::TYPE_PRINT: - echo $frame->scope[$op->arg2]->toString(); - $frame->scope[$op->arg1]->int(1); - break; - case OpCode::TYPE_JUMP: - $frame = $op->block1->getFrame( - $this->context, - $frame - ); - goto restart; - case OpCode::TYPE_JUMPIF: - $arg1 = $frame->scope[$op->arg1]->toBool(); - if ($arg1) { - $frame = $op->block1->getFrame($this->context, $frame); - } else { - $frame = $op->block2->getFrame($this->context, $frame); - } - goto restart; - case OpCode::TYPE_CASE: - $arg1 = $frame->scope[$op->arg1]; - $arg2 = $frame->scope[$op->arg2]; - if ($arg1->equals($arg2)) { - $frame = $op->block1->getFrame($this->context, $frame); - goto restart; - } - break; - case OpCode::TYPE_CONST_FETCH: - $value = null; - if (!is_null($op->arg3)) { - // try NS constant fetch - $value = $this->context->constantFetch($frame->scope[$op->arg3]->toString()); - } - if (is_null($value)) { - $value = $this->context->constantFetch($frame->scope[$op->arg2]->toString()); - } - if (is_null($value)) { - return $this->raise('Unknown constant fetch', $frame); - } - $frame->scope[$op->arg1]->copyFrom($value); - break; - case OpCode::TYPE_RETURN_VOID: - // TODO - goto nextframe; - case OpCode::TYPE_RETURN: - if (is_null($frame->returnVar)) { - var_dump($frame); - } - $frame->returnVar->copyFrom($frame->scope[$op->arg1]); - goto nextframe; - case OpCode::TYPE_FUNCDEF: - $name = $frame->scope[$op->arg1]->toString(); - $lcname = strtolower($name); - if (isset($this->context->functions[$lcname])) { - throw new \LogicException("Duplicate function definition for $lcname()"); - } - $this->context->declareFunction(new Func\PHP($name, $op->block1)); - break; - case OpCode::TYPE_FUNCCALL_INIT: - $name = $frame->scope[$op->arg1]->toString(); - $lcname = strtolower($name); - if (!isset($this->context->functions[$lcname])) { - throw new \LogicException("Call to undefined function $lcname()"); - } - $frame->call = $this->context->functions[$lcname]; - $frame->callArgs = []; - break; - case OpCode::TYPE_ARG_SEND: - $frame->callArgs[] = $frame->scope[$op->arg1]; - break; - case OpCode::TYPE_FUNCCALL_EXEC_RETURN: - case OpCode::TYPE_FUNCCALL_EXEC_NORETURN: - if (is_null($frame->call)) { - // Used for null constructors, etc - break; - } - $new = $frame->call->getFrame($this->context); - if ($op->type === OpCode::TYPE_FUNCCALL_EXEC_RETURN) { - $new->returnVar = $frame->scope[$op->arg1]; - } - $new->calledArgs = $frame->callArgs; - if ($new->hasHandler()) { - $new->handler->execute($new); - break; - } - $this->context->push($frame); - $frame = $new; - goto restart; - case OpCode::TYPE_ARG_RECV: - // Todo: do type checks and transformations - $arg1 = $frame->scope[$op->arg1]; - $arg1->copyFrom($frame->calledArgs[$op->arg2]); - break; - case OpCode::TYPE_DECLARE_CLASS: - $name = $frame->scope[$op->arg1]->toString(); - $lcname = strtolower($name); - if (isset($this->context->classes[$lcname])) { - throw new \LogicException("Duplicate class definition for $name"); - } - $classEntry = new ClassEntry($name); - self::defineClass($classEntry, $op->block1); - $this->context->classes[$lcname] = $classEntry; - break; - case OpCode::TYPE_NEW: - $result = $frame->scope[$op->arg1]; - $name = $frame->scope[$op->arg2]->toString(); - $lcname = strtolower($name); - if (!isset($this->context->classes[$lcname])) { - throw new \LogicException("Attempting to instantiate non-existing class $name"); - } - $class = $this->context->classes[$lcname]; - $result->object(new ObjectEntry($class)); - $frame->call = $result->toObject()->constructor; - $frame->callArgs = [$result]; - break; - case OpCode::TYPE_PROPERTY_FETCH: - $result = $frame->scope[$op->arg1]; - $var = $frame->scope[$op->arg2]->resolveIndirect(); - $name = $frame->scope[$op->arg3]->toString(); - if ($var->type !== Variable::TYPE_OBJECT) { - throw new \LogicException("Unsupported property fetch on non-object"); - } - $result->indirect($var->toObject()->getProperty($name)); - break; - case OpCode::TYPE_INIT_ARRAY: - $result = $frame->scope[$op->arg1]; - $result->newArray(); - if (is_null($op->arg2)) { - break; - } - // Fall through intentional - case OpCode::TYPE_ADD_ARRAY_ELEMENT: - $result = $frame->scope[$op->arg1]; - $ht = $result->toArray(); - if (is_null($op->arg3)) { - $ht->append($frame->scope[$op->arg2]); - break; - } - $key = $frame->scope[$op->arg3]->resolveIndirect(); - if ($key->is(Variable::TYPE_INTEGER)) { - $ht->addIndex($key->toInt(), $frame->scope[$op->arg2]); - } else { - $ht->add($key->toString(), $frame->scope[$op->arg2]); - } - break; - case OpCode::TYPE_BOOLEAN_NOT: - $value = !($frame->scope[$op->arg2]->toBool()); - $dst = $frame->scope[$op->arg1]; - $dst->bool($value); - break; - default: - throw new \LogicException("VM OpCode Not Implemented: " . $op->getType()); - } - } - return self::SUCCESS; - } - - protected function defineClass(ClassEntry $entry, Block $block): void { - $frame = $block->getFrame($this->context); - // TODO - foreach ($block->opCodes as $op) { - switch ($op->type) { - case OpCode::TYPE_DECLARE_PROPERTY: - $name = $frame->scope[$op->arg1]; - assert(is_null($op->arg2)); // no defaults for now - $entry->properties[] = new VM\ClassProperty( - $name->toString(), - null, - $frame->scope[$op->arg3] - ); - break; - default: - var_dump($op); - throw new \LogicException('Other class body types are not jittable for now'); - } - - } - } - -} diff --git a/lib/VM/ClassEntry.php b/lib/VM/ClassEntry.php deleted file mode 100755 index 434e6de..0000000 --- a/lib/VM/ClassEntry.php +++ /dev/null @@ -1,33 +0,0 @@ -name = $name; - } - - public function getProperties(array $properties, int $reason): array { - // todo: implement __debug_info - return $properties; - } - -} diff --git a/lib/VM/ClassProperty.php b/lib/VM/ClassProperty.php deleted file mode 100755 index 95a8d6e..0000000 --- a/lib/VM/ClassProperty.php +++ /dev/null @@ -1,34 +0,0 @@ -name = $name; - $this->default = $default; - $this->prototype = $prototype; - } - - public function getVariable(): Variable { - $var = clone $this->prototype; - if (!is_null($this->default)) { - $var->copyFrom($this->default); - } - - return $var; - } - - -} diff --git a/lib/VM/Context.php b/lib/VM/Context.php deleted file mode 100755 index be75eac..0000000 --- a/lib/VM/Context.php +++ /dev/null @@ -1,91 +0,0 @@ -runtime = $runtime; - } - - public function constantFetch(string $name): ?Variable { - switch (strtolower($name)) { - case 'null': - return new Variable(Variable::TYPE_NULL); - case 'false': - $var = new Variable(Variable::TYPE_BOOLEAN); - $var->bool(false); - return $var; - case 'true': - $var = new Variable(Variable::TYPE_BOOLEAN); - $var->bool(true); - return $var; - } - if (isset($this->constants[$name])) { - return $this->constants[$name]; - } - return null; - } - - public function declareFunction(Func $func): void { - $lcname = strtolower($func->getName()); - $this->functions[$lcname] = $func; - } - - public function save(Frame $frame): RunStackEntry { - $this->push($frame); - $return = $this->runStack; - $this->runStack = null; - return $return; - } - - public function restore(RunStackEntry $runStack): Frame { - assert(is_null($this->runStack)); - $this->runStack = $runStack->prev; - return $runStack->frame; - } - - public function push(Frame $frame): void { - if (is_null($this->runStack)) { - $this->runStack = new RunStackEntry($frame); - } else { - $this->runStack = $this->runStack->prev = new RunStackEntry($frame); - } - } - - public function pop(): ?Frame { - $return = $this->runStack; - if (!is_null($this->runStack)) { - $this->runStack = $this->runStack->prev; - return $return->frame; - } - return null;; - } -} - -class RunStackEntry { - public ?RunStackEntry $prev = null; - public Frame $frame; - - public function __construct(Frame $frame) { - $this->frame = $frame; - } -} diff --git a/lib/VM/HashTable.php b/lib/VM/HashTable.php deleted file mode 100755 index f443a6c..0000000 --- a/lib/VM/HashTable.php +++ /dev/null @@ -1,320 +0,0 @@ -refcount = new Refcount; - $this->flags = self::FLAG_UNINITIALIZED; - $this->indexes = MaskedArray::allocate(self::MIN_SIZE); - $this->buckets = MaskedArray::allocate(self::MIN_SIZE); - } - - public function iterate(bool $resolveIndirect = false): \Traversable { - for ($i = 0; $i < $this->numUsed; $i++) { - $bucket = $this->buckets->read($i); - if ($bucket->value->isUndefined()) { - continue; - } - $value = $bucket->value; - if ($resolveIndirect) { - $value = $value->resolveIndirect; - } - yield $value; - } - } - - public function findVariable(Variable $index, bool $forWrite): ?Variable { - switch ($index->type) { - case Variable::TYPE_INTEGER: - $result = $this->findIndex($index->toInt()); - break; - case Variable::TYPE_STRING: - $result = $this->find($index->toString()); - break; - default: - throw new \LogicException("Unknown index type {$index->type}"); - } - if (is_null($result)) { - $result = new Variable; - if ($forWrite) { - if ($index->type === Variable::TYPE_INTEGER) { - return $this->addIndex($index->toInt(), $result); - } else { - return $this->add($index->toString(), $result); - } - } - } - return $result; - } - - public function findIndex(int $index): ?Variable { - $this->assertConsistent(); - $bucket = $this->findBucket($index, null); - if (is_null($bucket)) { - return null; - } - return $bucket->value; - } - - public function find(string $key): ?Variable { - $this->assertConsistent(); - $bucket = $this->findBucket($this->hash($key), $key); - if (is_null($bucket)) { - return null; - } - return $bucket->value; - } - - public function append(Variable $data): ?Variable { - return $this->addOrUpdate($this->nextFreeElement, null, $data, self::ADD | self::ADD_NEXT); - } - - public function addIndex(int $index, Variable $data): ?Variable { - return $this->addOrUpdate($index, null, $data, self::ADD); - } - - public function addNewIndex(int $index, Variable $data): ?Variable { - return $this->addOrUpdate($index, null, $data, self::ADD | self::ADD_NEW); - } - - public function updateIndex(int $index, Variable $data): ?Variable { - return $this->addOrUpdate($index, null, $data, self::UPDATE); - } - - public function updateIndirectIndex(int $index, Variable $data): ?Variable { - return $this->addOrUpdate($index, null, $data, self::UPDATE | self::UPDATE_INDIRECT); - } - - public function add(string $key, Variable $data): ?Variable { - return $this->addOrUpdate($this->hash($key), $key, $data, self::ADD); - } - - public function addNew(string $key, Variable $data): ?Variable { - return $this->addOrUpdate($this->hash($key), $key, $data, self::ADD_NEW); - } - - public function update(string $key, Variable $data): ?Variable { - return $this->addOrUpdate($this->hash($key), $key, $data, self::UPDATE); - } - - public function updateIndirect(string $key, Variable $data): ?Variable { - return $this->addOrUpdate($this->hash($key), $key, $data, self::UPDATE | self::UPDATE_INDIRECT); - } - - private function addOrUpdate(int $hash, ?string $key, Variable $data, int $flags): ?Variable { - $this->assertConsistent(); - $this->refcount->assertSeparated(); - if ($this->flags & self::FLAG_UNINITIALIZED) { - $this->initMixed(); - } - $this->resizeIfFull(); - if (($flags & self::ADD_NEW) === 0) { - $bucket = $this->findBucket($hash, $key); - if ($bucket) { - if ($flags & self::ADD) { - if (!($flags & self::UPDATE_INDIRECT)) { - return null; - } - $bucketData = $bucket->value; - if ($bucketData->isIndirect()) { - $bucketData = $bucketData->resolveIndirect(); - if (!$bucketData->isUndefined()) { - return null; - } - } else { - return null; - } - } else { - $bucketData = $bucket->value; - if (($flags & self::UPDATE_INDIRECT) && $bucketData->isIndirect()) { - $bucketData = $bucketData->resolveIndirect(); - } - } - $bucketData->copyFrom($data); - return $bucketData; - } - } - $this->resizeIfFull(); - $id = $this->numUsed++; - $this->numElements++; - $bucket = $this->buckets->read($id); - $bucket->key = $key; - $bucket->hash = $hash; - $bucket->value->next = $this->indexes->read($hash); - $this->indexes->write($hash, $id); - $bucket->value->copyFrom($data); - if (is_null($key) && $hash >= $this->nextFreeElement) { - $this->nextFreeElement = $hash + 1; - } - return $bucket->value; - } - - private function findBucket(int $hash, ?string $key): ?HashTableBucket { - $idx = $this->indexes->read($hash); - do { - if ($idx === self::INVALID_INDEX) { - return null; - } - $bucket = $this->buckets->read($idx); - if ($bucket->key === $key) { - return $bucket; - } - $idx = $bucket->value->next; - } while (true); - } - - private function assertUninitialized(): void { - if (0 === ($this->flags & self::FLAG_UNINITIALIZED)) { - throw new \LogicException('Hash table was asserted to be uninitialized, but was initialized'); - } - } - - private function assertConsistent(): void { - if (($this->flags & self::FLAG_CONSISTENCY) === self::OKAY) { - return; - } - switch ($this->flags & self::FLAG_CONSISTENCY) { - case self::IS_DESTROYING: - throw new \LogicException('Hash table is being destroyed'); - case self::DESTROYED: - throw new \LogicException('Hash table is already destroyed'); - case self::CLEANING: - throw new \LogicException('Hash table is being cleaned'); - } - // Should never happen - throw new \LogicException('Hash table is inconsistent'); - } - - private function init(bool $packed) { - $this->refcount->assertSeparated(); - $this->assertUninitialized(); - if ($packed) { - $this->initPacked(); - } else { - $this->initMixed(); - } - } - - private function initMixed(): void { - $this->flags = $this->flags & ~self::FLAG_UNINITIALIZED; - $this->rehash(); - } - - private function resizeIfFull(): void { - if ($this->numUsed >= $this->indexes->size()) { - $this->resize(); - } - } - - private function resize(): void { - if ($this->numUsed > $this->numElements + ($this->numElements >> 5)) { - $this->rehash(); - return; - } - $oldSize = $this->indexes->size(); - $this->indexes->grow(); // increase by factor of 2 - $this->buckets->grow(); - $newSize = $this->indexes->size(); - for ($i = $oldSize; $i < $newSize; $i++) { - $this->indexes->write($i, self::INVALID_INDEX); - $this->buckets->write($i, new HashTableBucket(new Variable(Variable::TYPE_UNDEFINED), 0, null)); - } - $this->rehash(); - } - - private function rehash(): void { - if ($this->numElements === 0) { - if (!($this->flags & self::FLAG_UNINITIALIZED)) { - $this->numUsed = 0; - for ($i = 0, $n = $this->indexes->size(); $i < $n; $i++) { - $this->indexes->write($i, self::INVALID_INDEX); - $this->buckets->write($i, new HashTableBucket(new Variable(Variable::TYPE_UNDEFINED), 0, null)); - } - } - return; - } - $this->reset(); - $bucketIndex = 0; - if ($this->isWithoutHoles()) { - do { - $bucket = $this->buckets->read($bucketIndex); - $index = $bucket->hash; - $bucket->value->next = $index; - $this->indexes->write($index, $bucketIndex); - } while (++$bucketIndex < $this->numUsed); - return; - } - //todo - throw new \LogicException('Need to implement rehash'); - } - - private function isWithoutHoles(): bool { - return $this->numUsed === $this->numElements; - } - - private function reset() { - for ($i = 0, $n = $this->indexes->size(); $i < $n; $i++) { - $this->indexes->write($i, self::INVALID_INDEX); - } - } - - private function hash(string $key): int { - $hash = 5381; - for ($i = 0, $len = strlen($key); $i < $len; $i++) { - $hash = (($hash << 5) + $hash) + ord($key[$i]); - } - return $hash | 0x8000000000000000; - } -} - -final class HashTableBucket { - public Variable $value; - public int $hash; - public ?string $key; - - public function __construct(Variable $value, int $hash, ?string $key) { - $this->value = $value; - $this->hash = $hash; - $this->key = $key; - } -} diff --git a/lib/VM/ObjectEntry.php b/lib/VM/ObjectEntry.php deleted file mode 100755 index eb7736b..0000000 --- a/lib/VM/ObjectEntry.php +++ /dev/null @@ -1,43 +0,0 @@ -class = $class; - $this->id = ++self::$counter; - foreach ($class->properties as $property) { - $this->properties[$property->name] = $property->getVariable(); - } - } - - public function getProperty(string $name): Variable { - if (!isset($this->properties[$name])) { - throw new \LogicException("Undefined property access"); - } - return $this->properties[$name]; - } - - public function getProperties(int $purpose): array { - return $this->class->getProperties($this->properties, $purpose); - } - -} diff --git a/lib/VM/Optimizer/AssignOp.php b/lib/VM/Optimizer/AssignOp.php deleted file mode 100755 index ec61064..0000000 --- a/lib/VM/Optimizer/AssignOp.php +++ /dev/null @@ -1,66 +0,0 @@ -contains($block)) { - return; - } - $seen->attach($block); - $prior = null; - $toRemove = []; - foreach ($block->opCodes as $key => $op) { - if ($op->type === OpCode::TYPE_ASSIGN && null !== $prior && in_array($prior->type, self::CANDIDATE_OPS, true)) { - // replace - $binaryOpResult = $block->getOperand($prior->arg1); - if (count($binaryOpResult->usages) === 1) { - // We can safely replace it with an assign op - $prior->arg1 = $op->arg2; - $assignResult = $block->getOperand($op->arg1); - if (empty($assignResult->usages)) { - // remove assign as it's dead - $toRemove[] = $key; - } else { - // We still need the assign, since we're using the result - $op->arg2 = $op->arg1; - } - } - } - $prior = $op; - if (null !== $op->block1) { - $this->optimize($op->block1, $seen); - } - if (null !== $op->block2) { - $this->optimize($op->block2, $seen); - } - } - - if (! empty($toRemove)) { - foreach ($toRemove as $key) { - unset($block->opCodes[$key]); - } - $block->opCodes = array_values($block->opCodes); - } - } -} diff --git a/lib/VM/Refcount.php b/lib/VM/Refcount.php deleted file mode 100755 index 1213e76..0000000 --- a/lib/VM/Refcount.php +++ /dev/null @@ -1,21 +0,0 @@ -refcount > 1) { - throw new \LogicException('Refcount is > 1, but was asserted to be 1'); - } - } - -} \ No newline at end of file diff --git a/lib/VM/Variable.php b/lib/VM/Variable.php deleted file mode 100755 index c72309b..0000000 --- a/lib/VM/Variable.php +++ /dev/null @@ -1,546 +0,0 @@ -type = $type; - } - - public static function mapFromType(Type $type): int { - switch ($type->type) { - case Type::TYPE_NULL: - return self::TYPE_NULL; - case Type::TYPE_LONG: - return self::TYPE_INTEGER; - case Type::TYPE_DOUBLE: - return self::TYPE_FLOAT; - case Type::TYPE_BOOLEAN: - return self::TYPE_BOOLEAN; - case Type::TYPE_OBJECT: - return self::TYPE_OBJECT; - case Type::TYPE_STRING: - return self::TYPE_STRING; - case Type::TYPE_ARRAY: - return self::TYPE_ARRAY; - } - return self::TYPE_UNKNOWN; - } - - public function isUndefined(): bool { - return $this->type === self::TYPE_UNDEFINED; - } - - public function resolveIndirect(): self { - $var = $this; - while ($var->type === self::TYPE_INDIRECT) { - $var = $var->indirect; - } - return $var; - } - - public function newArray(): HashTable { - $this->array(new HashTable); - return $this->array; - } - - public function array(HashTable $ht): void { - $this->reset(); - $this->type = self::TYPE_ARRAY; - $this->array = $ht; - } - - public function toArray(): HashTable { - switch ($this->type) { - case self::TYPE_NULL: - return new HashTable; - case self::TYPE_ARRAY: - return $this->array; - } - $ht = new HashTable; - $ht->append($this); - return $ht; - } - - public function int(int $value): void { - $this->reset(); - $this->type = self::TYPE_INTEGER; - $this->integer = $value; - } - - public function is(int $type): bool { - if ($this->type === $type) { - return true; - } - if ($this->type === self::TYPE_INDIRECT) { - return $this->indirect->is($type); - } - return false; - } - - public function toInt(): int { - switch ($this->type) { - case self::TYPE_NULL: - return 0; - case self::TYPE_INTEGER: - return $this->integer; - case self::TYPE_FLOAT: - return (int) $this->float; - case self::TYPE_BOOLEAN: - return $this->bool ? 1 : 0; - case self::TYPE_STRING: - return (int) $this->string; - case self::TYPE_INDIRECT: - return $this->indirect->toInt(); - } - } - - public function float(float $value): void { - $this->reset(); - $this->type = self::TYPE_FLOAT; - $this->float = $value; - } - - public function toFloat(): float { - switch ($this->type) { - case self::TYPE_NULL: - return 0; - case self::TYPE_INTEGER: - return (float) $this->integer; - case self::TYPE_FLOAT: - return $this->float; - case self::TYPE_BOOLEAN: - return $this->bool ? 1.0 : 0.0; - case self::TYPE_STRING: - return (float) $this->string; - case self::TYPE_INDIRECT: - return $this->indirect->toFloat(); - } - } - - public function toNumeric() { - switch ($this->type) { - case self::TYPE_NULL: - return 0; - case self::TYPE_INTEGER: - return $this->integer; - case self::TYPE_FLOAT: - return $this->float; - case self::TYPE_BOOLEAN: - return $this->bool ? 1 : 0; - case self::TYPE_STRING: - if (!is_numeric($this->string)) { - throw new \LogicException("Cannot convert string to numeric"); - } - if (((string)(int) $this->string) === $this->string) { - return (int) $this->string; - } - return (float) $this->string; - case self::TYPE_INDIRECT: - return $this->indirect->toNumeric(); - } - throw new \LogicException("Not implemented numeric conversion: $this->type"); - } - - - public function null(): void { - $this->reset(); - $this->type = self::TYPE_NULL; - } - - public function bool(bool $value): void { - $this->reset(); - $this->type = self::TYPE_BOOLEAN; - $this->bool = $value; - } - - public function toBool(): bool { - switch ($this->type) { - case self::TYPE_NULL: - return false; - case self::TYPE_INTEGER: - return 0 !== $this->integer; - case self::TYPE_FLOAT: - return 0 !== $this->float; - case self::TYPE_BOOLEAN: - return $this->bool; - case self::TYPE_STRING: - return '' === $this->string || '0' === $this->string; - case self::TYPE_INDIRECT: - return $this->indirect->toBool(); - } - } - - public function string(string $value): void { - $this->reset(); - $this->type = self::TYPE_STRING; - $this->string = $value; - } - - public function toString(): string { - switch ($this->type) { - case self::TYPE_STRING: - return $this->string; - case self::TYPE_INTEGER: - return (string) $this->integer; - case self::TYPE_FLOAT: - return (string) $this->float; - case self::TYPE_BOOLEAN: - return $this->bool ? '1' : ''; - case self::TYPE_INDIRECT: - return $this->indirect->toString(); - case self::TYPE_ARRAY: - // todo: raise notice - return 'Array'; - } - } - - public function object(ObjectEntry $value): void { - $this->reset(); - $this->type = self::TYPE_OBJECT; - $this->object = $value; - } - - public function toObject(): ObjectEntry { - switch ($this->type) { - case self::TYPE_OBJECT: - return $this->object; - case self::TYPE_INDIRECT: - return $this->indirect->toObject(); - } - throw new \LogicException("Cannot convert $this->type to Object"); - } - - public function indirect(Variable $value): void { - $this->reset(); - $this->type = self::TYPE_INDIRECT; - $this->indirect = $value; - } - - public function reset(): void { - $this->type = self::TYPE_NULL; - unset($this->string); - unset($this->integer); - unset($this->float); - unset($this->bool); - unset($this->object); - unset($this->indirect); - } - - public function castFrom(int $type, self $var) { - $this->reset(); - $this->type = $type; - switch ($type) { - case Variable::NUMERIC: - $number = $var->toNumeric(); - if (is_int($number)) { - $this->castFrom(Variable::TYPE_INTEGER, $var); - } else { - $this->castFrom(Variable::TYPE_FLOAT, $var); - } - case Variable::TYPE_INTEGER: - $this->integer = $var->toInt(); - break; - case Variable::TYPE_FLOAT: - $this->float = $var->toFloat(); - break; - case Variable::TYPE_BOOLEAN: - $this->bool = $var->toBool(); - break; - case Variable::TYPE_STRING: - $this->string = $var->toString(); - break; - default: - throw new \LogicException("Unsupported cast type $type"); - } - } - - public function copyFrom(self $var): void { - if ($this->type === self::TYPE_INDIRECT) { - // always assign to the indirection - $this->indirect->copyFrom($var); - return; - } - while ($var->type === self::TYPE_INDIRECT) { - // destroy the indirection - $var = $var->indirect; - } - switch ($var->type) { - case self::TYPE_NULL: - $this->null(); - break; - case self::TYPE_STRING: - $this->string($var->string); - break; - case self::TYPE_INTEGER: - $this->int($var->integer); - break; - case self::TYPE_FLOAT: - $this->float($var->float); - break; - case self::TYPE_BOOLEAN: - $this->bool($var->bool); - break; - case self::TYPE_OBJECT: - $this->object($var->object); - break; - case self::TYPE_ARRAY: - $this->array($var->array); - break; - default: - var_dump($var); - throw new \LogicException("Unsupported type copy: {$var->type}"); - } - } - - public function identicalTo(Variable $other): bool { - $self = $this->resolveIndirect(); - $other = $other->resolveIndirect(); - if ($self->type !== $other->type) { - return false; - } - return $self->equals($other); - } - - public function equals(Variable $other): bool { - $self = $this; -restart: - $pair = type_pair($self->type, $other->type); - switch ($pair) { - case TYPE_PAIR_INTEGER_INTEGER: - return $self->integer === $other->integer; - case TYPE_PAIR_FLOAT_FLOAT: - return $self->float === $other->float; - case TYPE_PAIR_STRING_STRING: - return $self->string === $other->string; - case TYPE_PAIR_OBJECT_OBJECT: - return $self->object === $other->object; - case TYPE_PAIR_INTEGER_FLOAT: - return ((float) $self->integer) === $other->float; - case TYPE_PAIR_FLOAT_INTEGER: - return $self->float === ((float) $other->integer); - default: - if ($self->type === self::TYPE_INDIRECT) { - $self = $self->indirect; - goto restart; - } elseif ($other->type === self::TYPE_INDIRECT) { - $other = $other->indirect; - goto restart; - } - } - throw new \LogicException("Equals comparison between {$self->type} and {$other->type} not implemented"); - } - - public function compareOp(int $opCode, Variable $left, Variable $right): void { - $this->reset(); -restart: - switch (type_pair($left->type, $right->type)) { - case TYPE_PAIR_INTEGER_INTEGER: - $this->bool($this->_compareOp($opCode, $left->integer, $right->integer)); - break; - case TYPE_PAIR_INTEGER_FLOAT: - $this->bool($this->_compareOp($opCode, $left->integer, $right->float)); - break; - case TYPE_PAIR_FLOAT_INTEGER: - $this->bool($this->_compareOp($opCode, $left->float, $right->integer)); - break; - case TYPE_PAIR_FLOAT_FLOAT: - $this->bool($this->_compareOp($opCode, $left->float, $right->float)); - break; - case TYPE_PAIR_STRING_STRING: - $this->bool($this->_compareOp($opCode, $left->string, $right->string)); - break; - default: - if ($left->type === self::TYPE_INDIRECT) { - $left = $left->indirect; - goto restart; - } elseif ($right->type === self::TYPE_INDIRECT) { - $right = $right->indirect; - goto restart; - } else { - $this->bool($this->_compareOp($opCode, $left->toNumeric(), $right->toNumeric())); - } - } - } - - private function _compareOp(int $opCode, $left, $right): bool { - switch ($opCode) { - case OpCode::TYPE_IDENTICAL: - return $left === $right; - case OpCode::TYPE_GREATER: - return $left > $right; - case OpCode::TYPE_SMALLER: - return $left < $right; - case OpCode::TYPE_GREATER_OR_EQUAL: - return $left >= $right; - case OpCode::TYPE_SMALLER_OR_EQUAL: - return $left <= $right; - default: - throw new \LogicException("Non-implemented numeric comparison operation $opCode"); - } - } - - public function bitwiseOp(int $opCode, Variable $left, Variable $right): void { - $this->reset(); -restart: - $pair = type_pair($left->type, $right->type); - if ($pair === TYPE_PAIR_INTEGER_INTEGER) { - $result = $this->_bitwiseOp($opCode, $left->integer, $right->integer); - if (is_int($result)) { - $this->int($result); - } else { - $this->float($result); - } - } elseif ($pair === TYPE_PAIR_INTEGER_FLOAT) { - $this->float($this->_bitwiseOp($opCode, $left->integer, $right->float)); - } elseif ($pair === TYPE_PAIR_FLOAT_INTEGER) { - $this->float($this->_bitwiseOp($opCode, $left->float, $right->integer)); - } elseif ($pair === TYPE_PAIR_FLOAT_FLOAT) { - $this->float($this->_bitwiseOp($opCode, $left->float, $right->float)); - } elseif ($left->type === self::TYPE_INDIRECT) { - $left = $left->indirect; - goto restart; - } elseif ($right->type === self::TYPE_INDIRECT) { - $right = $right->indirect; - goto restart; - } else { - $this->string($this->_bitwiseOp($opCode, $left->toString(), $right->toString())); - } - } - - private function _bitwiseOp(int $opCode, $left, $right) { - switch ($opCode) { - case OpCode::TYPE_BITWISE_AND: - return $left & $right; - case OpCode::TYPE_BITWISE_OR: - return $left | $right; - case OpCode::TYPE_BITWISE_XOR: - return $left ^ $right; - default: - throw new \LogicException("Non-implemented bitwise operation $opCode"); - } - } - - public function numericOp(int $opCode, Variable $left, Variable $right): void { - $this->reset(); -restart: - $pair = type_pair($left->type, $right->type); - if ($pair === TYPE_PAIR_INTEGER_INTEGER) { - $result = $this->_numericOp($opCode, $left->integer, $right->integer); - if (is_int($result)) { - $this->int($result); - } else { - $this->float($result); - } - } elseif ($pair === TYPE_PAIR_INTEGER_FLOAT) { - $this->float($this->_numericOp($opCode, $left->integer, $right->float)); - } elseif ($pair === TYPE_PAIR_FLOAT_INTEGER) { - $this->float($this->_numericOp($opCode, $left->float, $right->integer)); - } elseif ($pair === TYPE_PAIR_FLOAT_FLOAT) { - $this->float($this->_numericOp($opCode, $left->float, $right->float)); - } elseif ($left->type === self::TYPE_INDIRECT) { - $left = $left->indirect; - goto restart; - } elseif ($right->type === self::TYPE_INDIRECT) { - $right = $right->indirect; - goto restart; - } else { - $result = $this->_numericOp($opCode, $left->toNumeric(), $right->toNumeric()); - if (is_int($result)) { - $this->int($result); - } else { - $this->float($result); - } - } - } - - private function _numericOp(int $opCode, $left, $right) { - switch ($opCode) { - case OpCode::TYPE_PLUS: - return $left + $right; - case OpCode::TYPE_MINUS: - return $left - $right; - case OpCode::TYPE_MUL: - return $left * $right; - case OpCode::TYPE_DIV: - return $left / $right; - default: - throw new \LogicException("Non-implemented numeric binary operation $opCode"); - } - } - - public function unaryOp(int $opCode, Variable $expr): void { - $this->reset(); -restart: - switch ($opCode) { - case OpCode::TYPE_UNARY_PLUS: - $this->castFrom(self::NUMERIC, $expr); - return; - case OpCode::TYPE_UNARY_MINUS: - if ($expr->type === Variable::TYPE_INTEGER) { - $this->copyFrom($expr); - $this->integer *= -1; - return; - } elseif($expr->type === Variable::TYPE_FLOAT) { - $this->copyFrom($expr); - $this->float *= -1.0; - return; - } else { - $this->castFrom(self::NUMERIC, $expr); - goto restart; - } - break; - } - throw new \LogicException("UnaryOp $opCode not implemented for type $expr->type"); - } -} - -const TYPE_PAIR_INTEGER_INTEGER = (Variable::TYPE_INTEGER << 8) | Variable::TYPE_INTEGER; -const TYPE_PAIR_FLOAT_INTEGER = (Variable::TYPE_FLOAT << 8) | Variable::TYPE_INTEGER; -const TYPE_PAIR_INTEGER_FLOAT = (Variable::TYPE_INTEGER << 8) | Variable::TYPE_FLOAT; -const TYPE_PAIR_FLOAT_FLOAT = (Variable::TYPE_FLOAT << 8) | Variable::TYPE_FLOAT; -const TYPE_PAIR_STRING_STRING = (Variable::TYPE_STRING << 8) | Variable::TYPE_STRING; -const TYPE_PAIR_OBJECT_OBJECT = (Variable::TYPE_OBJECT << 8) | Variable::TYPE_OBJECT; - -function type_pair(int $left, int $right): int { - return ($left << 8) | $right; -} diff --git a/lib/JIT/Variable.php b/lib/Variable.php similarity index 95% rename from lib/JIT/Variable.php rename to lib/Variable.php index 0841e34..6340b5c 100755 --- a/lib/JIT/Variable.php +++ b/lib/Variable.php @@ -7,12 +7,10 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCompiler\JIT; +namespace PHPCompiler; -use PHPCompiler\Block; use PHPCfg\Operand; use PHPTypes\Type; -use PHPCompiler\VM\Variable as VMVariable; use PHPLLVM; @@ -72,16 +70,6 @@ public function __construct( $this->value = $value; } - public static function fromVMVariable(int $type): int { - switch ($type) { - case VMVariable::TYPE_INTEGER: return self::TYPE_NATIVE_LONG; - case VMVariable::TYPE_FLOAT: return self::TYPE_NATIVE_DOUBLE; - case VMVariable::TYPE_BOOLEAN: return self::TYPE_NATIVE_BOOL; - case VMVariable::TYPE_STRING: return self::TYPE_STRING; - } - throw new \LogicException("Not implemented type conversion: $type"); - } - public static function getStringTypeFromType(Type $type): string { return self::getStringType(self::getTypeFromType($type)); }