Skip to content
This repository has been archived by the owner on Dec 29, 2022. It is now read-only.

Commit

Permalink
More PECL work, support string concatonation and garbage collection t…
Browse files Browse the repository at this point in the history
…hrough a free system built by a post-dominator
  • Loading branch information
ircmaxell committed Oct 22, 2014
1 parent e828682 commit 848e16e
Show file tree
Hide file tree
Showing 14 changed files with 383 additions and 24 deletions.
2 changes: 2 additions & 0 deletions lib/ReckiCT/Analyzer/Factory.php
Expand Up @@ -53,6 +53,8 @@ public static function analyzer()

$analyzer->addProcessor(new GraphProcessor\PhiResolver());

$analyzer->addProcessor(new GraphProcessor\FreeResolver());

return $analyzer;
}

Expand Down
79 changes: 79 additions & 0 deletions lib/ReckiCT/Analyzer/GraphProcessor/FreeResolver.php
@@ -0,0 +1,79 @@
<?php
/**
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @copyright 2014 Google Inc. All rights reserved
* @license http://www.apache.org/licenses/LICENSE-2.0.txt Apache-2.0
* @package Analyzer
* @subpackage GraphProcessor
*/

namespace ReckiCT\Analyzer\GraphProcessor;

use ReckiCT\Type;
use ReckiCT\Graph\Helper;
use ReckiCT\Graph\Vertex\Free;
use ReckiCT\Graph\Vertex\Jump;
use ReckiCT\Graph\Vertex\JumpZ;

use Gliph\Graph\DirectedAdjacencyList;

use ReckiCT\Analyzer\GraphState;

use ReckiCT\Analyzer\GraphProcessor;

class FreeResolver implements GraphProcessor
{
public function process(GraphState $state)
{
$graph = $state->getGraph();
$vars = Helper::findVariables($graph);
$postdominator = $state->getPostDominator();
$dominator = $state->getDominator();

foreach ($vars as $var) {
$usages = [];
foreach ($graph->vertices() as $vertex) {
if (in_array($var, $vertex->getVariables(), true)) {
$usages[] = $vertex;
}
}

$dom = $postdominator->immediateDominatorArray($usages);
while ($dom) {
foreach ($usages as $usage) {
if ($dominator->strictlyDominates($usage, $dom)) {
$dom = $postdominator->immediateDominator($dom);

continue 2;
}
}
break;
}

if (!$dom) {
continue;
}

if ($dom instanceof Jump || $dom instanceof JumpZ) {
Helper::insertBefore($dom, new Free($var), $graph);
} else {
Helper::insertAfter($dom, new Free($var), $graph);
}
}
}


}
3 changes: 3 additions & 0 deletions lib/ReckiCT/Analyzer/OptimizerRule/BinaryOp.php
Expand Up @@ -43,6 +43,9 @@ public function process(Vertex $vertex, Digraph $graph)
$typePair = [$vertex->getA()->getType()->getType(), $vertex->getB()->getType()->getType()];
$newType = Type::TYPE_UNKNOWN;
switch ($vertex->getKind()) {
case JitBinaryOp::CONCAT:
$newType = Type::TYPE_STRING;
break;
case JitBinaryOp::PLUS:
case JitBinaryOp::MINUS:
case JitBinaryOp::MUL:
Expand Down
3 changes: 3 additions & 0 deletions lib/ReckiCT/Analyzer/OptimizerRule/ConstBinaryOp.php
Expand Up @@ -42,6 +42,9 @@ public function process(Vertex $vertex, Digraph $graph)
if ($vertex instanceof JitBinaryOp && $vertex->getA() instanceof Constant && $vertex->getB() instanceof Constant) {
$ret = null;
switch ($vertex->getKind()) {
case JitBinaryOp::CONCAT:
$ret = $vertex->getA()->getValue() . $vertex->getB()->getValue();
break;
case JitBinaryOp::PLUS:
$ret = $vertex->getA()->getValue() + $vertex->getB()->getValue();
break;
Expand Down
88 changes: 73 additions & 15 deletions lib/ReckiCT/Compiler/PECL/Function_.php
Expand Up @@ -104,22 +104,41 @@ protected function generateProxyFunction() {
}
$code .= "\tint validReturn;\n";

$zppType = '';
$zppArgs = '';
$zppType_5 = '';
$zppArgs_5 = '';
$zppType_7 = '';
$zppArgs_7 = '';
$callArgs = '';

$code_5 = "";
$post_5 = "";
foreach ($this->params as $param) {
$code .= "\t" . $this->convertToCType($param[1]) . ' ' . $param[0] . ";\n";
$zppType .= $this->getZppFromType($param[1]);
$zppType = $this->getZppFromType($param[1]);

$callArgs .= $param[0] . ', ';
if ($zppType == 's') {
$zppArgs .= ', &' . $param[0] . '.string, &' . $param[0] . '.length';
$zppType_7 .= 'S';
$zppArgs_7 .= ', &' . $param[0];

$code_5 .= "\t" . 'char *' . $param[0] . "_val;\n";
$code_5 .= "\t" . 'int ' . $param[0] . "_len;\n";
$post_5 .= "\t" . $param[0] . ' = recki_string_init(' . $param[0] . '_val, (size_t) ' . $param[0] . "_len, 0);\n";
$zppType_5 .= 's';
$zppArgs_5 .= ', &' . $param[0] . "_val, &" . $param[0] . "_len";
} else {
$zppArgs .= ', &' . $param[0];
$zppType_5 .= $zppType;
$zppType_7 .= $zppType;
$zppArgs_5 .= ', &' . $param[0];
$zppArgs_7 .= ', &' . $param[0];
}

}
$code .= "\tif (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, \"{$zppType}\"{$zppArgs}) == FAILURE) {\n\t\treturn;\n\t}\n";
$code .= "#if PHP_VERSION_ID >= 70000\n";
$code .= "\tif (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, \"{$zppType_7}\"{$zppArgs_7}) == FAILURE) {\n\t\treturn;\n\t}\n";
$code .= "#else\n$code_5";
$code .= "\tif (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, \"{$zppType_5}\"{$zppArgs_5}) == FAILURE) {\n\t\treturn;\n\t}\n";
$code .= "$post_5";
$code .= "#endif\n";
if ($this->returnType != 'void') {
$code .= "\treckiretval = ";
}
Expand All @@ -142,6 +161,7 @@ protected function buildFunction(array $ir) {
}

$vars = [];
$string_constants = '';
$count = count($ir);
while (++$i < $count) {
$instruction = $ir[$i];
Expand All @@ -151,17 +171,38 @@ protected function buildFunction(array $ir) {
if ($instruction[2] === 'void') break;
$scope[$instruction[1]] = $this->convertToCLabel($instruction[1]);
$vars[$scope[$instruction[1]]] = $this->convertToCType($instruction[2]);

break;
case 'const':
$scope[$instruction[1]] = $this->printConstant($instruction);
if ($instruction[2] == "string") {
$scope[$instruction[1]] = $this->convertToCLabel($instruction[1]);
$vars[$scope[$instruction[1]]] = $this->convertToCType($instruction[2]);
$string_constants .= $scope[$instruction[1]] . ' = ' . $this->printConstant($instruction) . ";\n";
} else {
$scope[$instruction[1]] = $this->printConstant($instruction);
}
break;
case 'free':
if (isset($vars[$scope[$instruction[1]]]) && $vars[$scope[$instruction[1]]] === "reckistring *") {
$code .= "recki_string_release(" . $scope[$instruction[1]] . ");\n";
}
break;

case '~':
if ($vars[$scope[$instruction[2]]] === "reckistring *") {
throw new \RuntimeException("Negatting strings, hurts");
}
case '!':
if ($vars[$scope[$instruction[2]]] === "reckistring *") {
throw new \RuntimeException("Notting strings, hurts");
}
$prefix = $instruction[0];
case 'assign':
$code .= $scope[$instruction[2]] . '= ' . $prefix . $scope[$instruction[1]] . ";\n";
$v = $scope[$instruction[2]];
if ($vars[$scope[$instruction[2]]] === "reckistring *") {
$code .= "if ($v != NULL) {\n\trecki_string_release($v);\n}\n";
$code .= $scope[$instruction[2]] . ' = recki_string_copy(' . $scope[$instruction[1]] . ");\n";
} else {
$code .= $scope[$instruction[2]] . ' = ' . $prefix . $scope[$instruction[1]] . ";\n";
}
break;
case 'return':
$code .= "*validReturn = SUCCESS;\n";
Expand Down Expand Up @@ -220,10 +261,20 @@ protected function buildFunction(array $ir) {
case '>=':
$code .= $scope[$instruction[3]] . '=' . $scope[$instruction[1]] . $instruction[0] . $scope[$instruction[2]] . ";\n";
break;
case '.':
$v = $scope[$instruction[3]];
$l = $scope[$instruction[1]];
$r = $scope[$instruction[2]];
$code .= "{$v} = recki_string_alloc({$l}->len + {$r}->len, 0);
memcpy({$v}->val, {$l}->val, {$l}->len);
memcpy({$v}->val + {$l}->len, {$r}->val, {$r}->len);
{$v}->val[{$v}->len] = '\\0';\n";
break;
default:
throw new \RuntimeException("Unsupported compiler operation {$instruction[0]}");
}
}
$code = $string_constants . $code;
// Generate variable declarations
foreach ($vars as $name => $type) {
$code = $type . ' ' . $name . ";\n" . $code;
Expand All @@ -239,7 +290,7 @@ protected function convertToCType($what) {
case 'double':
return 'double';
case 'string':
return 'reckistring';
return 'reckistring *';
case 'bool':
return 'zend_bool';
case 'void':
Expand Down Expand Up @@ -267,7 +318,7 @@ protected function printConstant(array $const) {
return (int) $const[3];
case 'string':
$val = base64_decode($const[3]);
return '((reckistring){"' . addslashes($val) . '", ' . strlen($val) . '})';
return 'recki_string_init("' . addslashes($val) . '", ' . strlen($val) . ', 0)';
}
throw new \RuntimeException("Unknown constant type {$const[2]} with value {$const[3]}");
}
Expand All @@ -280,8 +331,15 @@ protected function generateRetvalStatement($type) {
return 'RETURN_DOUBLE(reckiretval);';
case 'zend_bool':
return 'RETURN_BOOL(reckiretval);';
case 'reckistring':
return 'RETURN_STRINGL(reckiretval.string, reckiretval.length, 1);';
case 'reckistring *':
return "
#if PHP_VERSION_ID >= 70000
RETURN_STR(reckiretval);
#else
RETVAL_STRINGL(reckiretval->val, reckiretval->len, 1);
recki_string_release(reckiretval);
return;
#endif\n";
}
throw new \RuntimeException('Retval Type Not Implemented: ' . $type);
}
Expand Down
82 changes: 78 additions & 4 deletions lib/ReckiCT/Compiler/PECL/Module.php
Expand Up @@ -56,11 +56,54 @@ public function generateCFile() {
#endif
#include "php.h"
#include "php_{$this->name}.h"
#include <stdint.h>
typedef struct _reckistring {
char *string;
int length;
} reckistring;
#if PHP_VERSION_ID >= 70000
#define reckistring zend_string
#define recki_string_init zend_string_init
#define recki_string_copy zend_string_copy
#else
#define reckistring recki_string
#define _STR_HEADER_SIZE XtOffsetOf(recki_string, val)
#define IS_STR_PERSISTENT 1<<0
#define GC_REFCOUNT(p) ((recki_refcounted*)(p))->refcount
#define GC_TYPE(p) ((recki_refcounted*)(p))->u.v.type
#define GC_FLAGS(p) ((recki_refcounted*)(p))->u.v.flags
#define GC_INFO(p) ((recki_refcounted*)(p))->u.v.gc_info
#define GC_TYPE_INFO(p) ((recki_refcounted*)(p))->u.type_info
zend_always_inline void recki_string_release(recki_string *s) {
if (--GC_REFCOUNT(s) == 0) {
pefree(s, GC_FLAGS(s) & IS_STR_PERSISTENT);
}
}
zend_always_inline recki_string *recki_string_copy(recki_string *s) {
GC_REFCOUNT(s)++;
return s;
}
zend_always_inline recki_string *recki_string_alloc(size_t len, int persistent) {
recki_string *ret = (recki_string*) pemalloc(ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + len + 1), persistent);
GC_REFCOUNT(ret) = 1;
GC_TYPE_INFO(ret) = IS_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << 8);
ret->h = 0;
ret->len = len;
return ret;
}
zend_always_inline recki_string *recki_string_init(const char *str, size_t len, int persistent) {
recki_string *ret = recki_string_alloc(len, persistent);
memcpy(ret->val, str, len);
ret->val[len] = '\\0';
return ret;
}
#endif
EOF;

Expand Down Expand Up @@ -147,6 +190,37 @@ public function generateHeaderFile() {
#define PHP_{$upperName}_VERSION "1.0"
#define PHP_{$upperName}_EXTNAME "{$this->name}"
# define RECKI_ENDIAN_LOHI_3(lo, mi, hi) lo; mi; hi;
#ifndef uint32_t
typedef unsigned int uint32_t;
#endif
#ifndef uint16_t
typedef unsigned short int uint16_t;
#endif
typedef struct _recki_refcounted {
uint32_t refcount;
union {
struct {
RECKI_ENDIAN_LOHI_3(
zend_uchar type,
zend_uchar flags,
uint16_t gc_info)
} v;
uint32_t type_info;
} u;
} recki_refcounted;
typedef struct _recki_string {
recki_refcounted gc;
zend_ulong h;
size_t len;
char val[1];
} recki_string;
extern zend_module_entry {$this->name}_module_entry;
#define phpext_{$this->name}_ptr &{$this->name}_module_entry
Expand Down

0 comments on commit 848e16e

Please sign in to comment.