Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'origin/nqp-optimizer'
This gives us an optimizer for NQP that removes some int to num and
num to int coercions if they are superfluous.
  • Loading branch information
timo committed Aug 26, 2013
2 parents a49aa93 + 3e45f29 commit f1d040d
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 2 deletions.
7 changes: 7 additions & 0 deletions src/NQP/Compiler.nqp
@@ -1,4 +1,9 @@
class NQP::Compiler is HLL::Compiler {
method optimize($past, *%adverbs) {
%adverbs<optimize> eq 'off'
?? $past
!! NQP::Optimizer.new.optimize($past, |%adverbs)
}
}

# Create and configure compiler object.
Expand All @@ -8,6 +13,8 @@ $nqpcomp.parsegrammar(NQP::Grammar);
$nqpcomp.parseactions(NQP::Actions);
hll-config($nqpcomp.config);

$nqpcomp.addstage('optimize', :after<ast>);

# Add extra command line options.
my @clo := $nqpcomp.commandline_options();
@clo.push('parsetrace');
Expand Down
131 changes: 131 additions & 0 deletions src/NQP/Optimizer.nqp
@@ -0,0 +1,131 @@
class NQP::Optimizer {
has @!block_stack;

method optimize($ast, *%adverbs) {
@!block_stack := [$ast[0]];
self.visit_children($ast);
$ast;
}

method visit_block($block) {
@!block_stack.push($block);

self.visit_children($block);

@!block_stack.pop();

$block;
}

method find_lex($name) {
my int $i := +@!block_stack;
while $i > 0 {
$i := $i - 1;
my %sym := @!block_stack[$i].symbol($name);
if +%sym {
return %sym;
}
}
NQPMu;
}

method find_sym($name) {
my %sym := self.find_lex($name);
if !(%sym =:= NQPMu) && nqp::existskey(%sym, 'value') {
return %sym<value>;
}
else {
nqp::die("No compile-time value for $name");
}
}

method visit_op($op) {
sub returns_int($node) {
if nqp::objprimspec($node.returns) == 1 {
return 1
}
if nqp::istype($node, QAST::Op) {
my $typeinfo := nqp::chars($node.op) >= 2
?? nqp::substr($node.op, nqp::chars($node.op) - 2, 2)
!! "";
if $typeinfo eq "_i" {
return 1
} elsif $node.op eq 'chars' || $node.op eq 'ord' || $node.op eq 'elems' {
return 1
}
} elsif nqp::istype($node, QAST::IVal) {
return 1
} elsif nqp::istype($node, QAST::Var) && $node.scope eq 'lexical' {
my %sym := self.find_lex($node.name);
if nqp::existskey(%sym, 'type') && nqp::objprimspec(%sym<type>) == 1 {
return 1
}
}
return 0;
}
self.visit_children($op);

my $typeinfo := nqp::chars($op.op) >= 2
?? nqp::substr($op.op, nqp::chars($op.op) - 2, 2)
!! "";
my $asm := nqp::substr($op.op, 0, 3);

try {
if $typeinfo eq '_n' && ($asm eq 'add' || $asm eq 'sub' || $asm eq 'mul') {
my $newopn := $asm ~ "_i";
if returns_int($op[0]) && returns_int($op[1]) {
my $newopn := $asm ~ "_i";
$op.op($newopn);
$op.returns(self.find_sym("int"));
} else {
$op.returns(self.find_sym("num"));
}
} elsif $typeinfo eq '_i' {
$op.returns(self.find_sym("num"));
} elsif $typeinfo eq '_s' {
$op.returns(self.find_sym("str"));
} elsif $op.op eq 'handle' {
return self.visit_handle($op);
} elsif $op.op eq 'numify' {
# if we can establish that the argument is a list, we are good
# to claim it returns an int.
if nqp::istype($op[0], QAST::Var) {
my $sigil := nqp::substr($op[0].name, 0, 1);
if $sigil eq '@' || $sigil eq '%' {
$op.returns(self.find_sym("int"))
}
}
}
CATCH {
}
}

$op;
}

method visit_handle($handle) {
self.visit_children($handle, :skip_selectors);
$handle;
}

method visit_children($node, :$skip_selectors) {
my int $i := 0;
unless nqp::isstr($node) {
while $i < +@($node) {
unless $skip_selectors && $i % 2 {
my $visit := $node[$i];
if nqp::istype($visit, QAST::Op) {
$node[$i] := self.visit_op($visit)
} elsif nqp::istype($visit, QAST::Block) {
$node[$i] := self.visit_block($visit)
} elsif nqp::istype($visit, QAST::Want) {
self.visit_children($visit, :skip_selectors)
} else {
self.visit_children($visit);
}
}
$i := $i + 1;
}
}
}
}
7 changes: 6 additions & 1 deletion src/QAST/IVal.nqp
@@ -1,7 +1,12 @@
class QAST::IVal is QAST::Node {
has int $!value;

method BUILD() {
nqp::bindattr(self, QAST::Node, '$!returns', int);
}

method value(*@value) { $!value := @value[0] if @value; $!value }

method substitute_inline_placeholders(@fillers) {
self
}
Expand Down
7 changes: 6 additions & 1 deletion src/QAST/NVal.nqp
@@ -1,7 +1,12 @@
class QAST::NVal is QAST::Node {
has num $!value;

method BUILD() {
nqp::bindattr(self, QAST::Node, '$!returns', num);
}

method value(*@value) { $!value := @value[0] if @value; $!value }

method substitute_inline_placeholders(@fillers) {
self
}
Expand Down
1 change: 1 addition & 0 deletions tools/build/Makefile-JVM.in
Expand Up @@ -130,6 +130,7 @@ NQP_SOURCES = \
src/NQP/World.nqp \
src/NQP/Grammar.nqp \
src/vm/jvm/NQP/Ops.nqp \
src/NQP/Optimizer.nqp \
src/NQP/Actions.nqp \
src/NQP/Compiler.nqp \

Expand Down
1 change: 1 addition & 0 deletions tools/build/Makefile-Parrot.in
Expand Up @@ -169,6 +169,7 @@ NQP_SOURCES = \
src/NQP/Grammar.nqp \
src/vm/parrot/NQP/Ops.nqp \
src/NQP/Actions.nqp \
src/NQP/Optimizer.nqp \
src/NQP/Compiler.nqp \

NQP_MO_SOURCES = src/how/Archetypes.nqp src/how/RoleToRoleApplier.nqp \
Expand Down

0 comments on commit f1d040d

Please sign in to comment.