-
-
Save timo/b68416dedda71b72a458 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use QAST; | |
plan(3); | |
# Following a test infrastructure. | |
sub compile_qast($qast) { | |
my $*QAST_BLOCK_NO_CLOSE := 1; | |
nqp::getcomp('nqp').compile($qast, :from('ast')); | |
} | |
sub is_qast($qast, $value, $desc) { | |
try { | |
my $code := compile_qast($qast); | |
my $result := $code(); | |
ok($result eq $value, $desc); | |
CATCH { ok(0, $desc ~ $!) } | |
} | |
} | |
sub is_qast_args($qast, @args, $value, $desc) { | |
try { | |
my $code := compile_qast($qast); | |
ok($code(|@args) eq $value, $desc); | |
CATCH { ok(0, $desc) } | |
} | |
} | |
sub test_qast_result($qast, $tester) { | |
try { | |
my $code := compile_qast($qast); | |
$tester($code()); | |
CATCH { ok(0, 'Compilation failure in test_qast_result: ' ~ $!) } | |
} | |
} | |
sub make_ref_type($name, $kind) { | |
my $class := nqp::newtype(NQPMu, 'NativeRef'); | |
my $info := nqp::hash(); | |
$info<nativeref> := nqp::hash(); | |
$info<nativeref><type> := $kind; | |
$info<nativeref><refkind> := 'lexical'; | |
nqp::composetype($class, $info); | |
return $class; | |
} | |
my $hllconfig := nqp::hash(); | |
$hllconfig<int_lex_ref> := make_ref_type('StubIntLexRef', int); | |
$hllconfig<num_lex_ref> := make_ref_type('StubNumLexRef', num); | |
$hllconfig<str_lex_ref> := make_ref_type('StubStrLexRef', str); | |
nqp::sethllconfig('nqp', $hllconfig); | |
is_qast( | |
QAST::CompUnit.new( :hll<nqp>, | |
QAST::Block.new( | |
QAST::Var.new( :name<intloc>, :scope<local>, :decl<var>, :returns(int) ), | |
QAST::Op.new( | |
:op<assign_i>, | |
QAST::Var.new( :name<intloc>, :scope<localref> ), | |
QAST::IVal.new( :value(23) ) | |
), | |
QAST::Var.new( :name<intloc>, :scope<local> ) | |
) | |
), | |
23, | |
'localref of type int with value 23 assigned to it' | |
); | |
is_qast( | |
QAST::CompUnit.new( :hll<nqp>, | |
QAST::Block.new( | |
QAST::Var.new( :name<numloc>, :scope<local>, :decl<var>, :returns(num) ), | |
QAST::Op.new( | |
:op<assign_i>, | |
QAST::Var.new( :name<numloc>, :scope<localref> ), | |
QAST::NVal.new( :value(99e2) ) | |
), | |
QAST::Var.new( :name<numloc>, :scope<local> ) | |
) | |
), | |
99e2, | |
'localref of type num with value 99e2 assigned to it' | |
); | |
is_qast( | |
QAST::CompUnit.new( :hll<nqp>, | |
QAST::Block.new( | |
QAST::Var.new( :name<strloc>, :scope<local>, :decl<var>, :returns(str) ), | |
QAST::Op.new( | |
:op<assign_i>, | |
QAST::Var.new( :name<strloc>, :scope<localref> ), | |
QAST::SVal.new( :value('What do we have here?') ) | |
), | |
QAST::Var.new( :name<strloc>, :scope<local> ) | |
) | |
), | |
'What do we have here?', | |
'localref of type str with a value assigned to it' | |
); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/core/NQPMu.nqp b/src/core/NQPMu.nqp | |
index 3fdde41..07d18ea 100644 | |
--- a/src/core/NQPMu.nqp | |
+++ b/src/core/NQPMu.nqp | |
@@ -133,6 +133,7 @@ nqp::sethllconfig('nqp', nqp::hash( | |
'array_iter', NQPArrayIter, | |
'hash_iter', NQPHashIter | |
)); | |
+ | |
#?endif | |
my class NQPLabel { } | |
diff --git a/src/vm/moar/QAST/QASTCompilerMAST.nqp b/src/vm/moar/QAST/QASTCompilerMAST.nqp | |
index b8fbf72..de77891 100644 | |
--- a/src/vm/moar/QAST/QASTCompilerMAST.nqp | |
+++ b/src/vm/moar/QAST/QASTCompilerMAST.nqp | |
@@ -98,6 +98,8 @@ my class MASTCompilerInstance { | |
has %!local_names_by_index; # Locals' names by their indexes | |
has %!locals; # Mapping of local names to locals | |
has %!local_kinds; # Mapping of local registers to kinds | |
+ has %!localrefs; # Mapping of localref names to locals | |
+ has %!localref_kinds; # Mapping of localref registers to kinds | |
has %!lexicals; # Mapping of lexical names to lexicals | |
has %!lexical_kinds; # Mapping of lexical names to kinds | |
has %!lexical_params; # Mapping of lexical param names to their initial result reg | |
@@ -124,6 +126,8 @@ my class MASTCompilerInstance { | |
%!local_names_by_index := nqp::hash(); | |
%!locals := nqp::hash(); | |
%!local_kinds := nqp::hash(); | |
+ %!localrefs := nqp::hash(); | |
+ %!localref_kinds := nqp::hash(); | |
%!lexicals := nqp::hash(); | |
%!lexical_kinds := nqp::hash(); | |
%!lexical_params := nqp::hash(); | |
@@ -205,9 +209,9 @@ my class MASTCompilerInstance { | |
method register_local($var, :$is_cont) { | |
my $name := $var.name; | |
my $temporary := ?$*INSTMT; | |
- if nqp::existskey(%!locals, $name) || | |
+ if nqp::existskey(%!locals, $name) || nqp::existskey(%!localrefs, $name) || | |
$temporary && nqp::existskey(%*STMTTEMPS, $name) { | |
- nqp::die("Local '$name' already declared"); | |
+ nqp::die("Local (or localref) '$name' already declared"); | |
} | |
my $kind := $!compiler.type_to_register_kind($var.returns); | |
%!local_kinds{$name} := $kind; | |
@@ -224,6 +228,25 @@ my class MASTCompilerInstance { | |
$local; | |
} | |
+ method register_localref($var) { | |
+ my $name := $var.name; | |
+ my $temporary := ?$*INSTMT; | |
+ if nqp::existskey(%!localrefs, $name) || nqp::existskey(%!locals, $name) || | |
+ $temporary && nqp::existskey(%*STMTTEMPS, $name) { | |
+ nqp::die("Localref (or local) '$name' already declared"); | |
+ } | |
+ my $kind := $!compiler.type_to_register_kind($var.returns); | |
+ %!localref_kinds{$name} := $kind; | |
+ # pass a 1 meaning get a Totally New MAST::Local | |
+ my $localref := $*REGALLOC.fresh_register($MVM_reg_obj, !$temporary); | |
+ %!localrefs{$name} := $localref; | |
+ %!local_names_by_index{$localref.index} := $name; | |
+ if $temporary { | |
+ %*STMTTEMPS{$name} := $localref; | |
+ } | |
+ $localref; | |
+ } | |
+ | |
# returns whether a MAST::Local is a variable in this block | |
method is_var($local) { | |
nqp::existskey(%!local_names_by_index, $local.index) | |
@@ -257,6 +280,8 @@ my class MASTCompilerInstance { | |
method lexicalrefs() { %!lexicalrefs } | |
method local($name) { %!locals{$name} } | |
method local_kind($name) { %!local_kinds{$name} } | |
+ method localref($name) { %!localrefs{$name} } | |
+ method localref_kind($name) { %!localref_kinds{$name} } | |
method lexical_kind($name) { %!lexical_kinds{$name} } | |
method lexical_kinds() { %!lexical_kinds } | |
method lexicalref_kind($name) { %!lexicalref_kinds{$name} } | |
@@ -1159,6 +1184,12 @@ my class MASTCompilerInstance { | |
'getlexref_ns' | |
]; | |
+ my @localref_opnames := [ | |
+ 'getregref_i', | |
+ 'getregref_n', | |
+ 'getregref_s' | |
+ ]; | |
+ | |
my @decont_opnames := [ | |
'decont_i', | |
'decont_n', | |
@@ -1233,8 +1264,11 @@ my class MASTCompilerInstance { | |
elsif $scope eq 'lexicalref' { | |
$*BLOCK.add_lexicalref($node); | |
} | |
+ elsif $scope eq 'localref' { | |
+ $*BLOCK.register_localref($node); | |
+ } | |
else { | |
- nqp::die("Cannot declare variable with scope '$scope'; use 'local' or 'lexical'"); | |
+ nqp::die("Cannot declare variable with scope '$scope'; use one of 'local', 'lexical', 'localref' or 'lexicalref'"); | |
} | |
} | |
elsif $decl eq 'static' { | |
@@ -1286,6 +1320,8 @@ my class MASTCompilerInstance { | |
my @ins; | |
if $scope eq 'local' { | |
my $local := $*BLOCK.local($name); | |
+ my $localref := $*BLOCK.localref($name); | |
+ | |
if $local { | |
$res_kind := $*BLOCK.local_kind($name); | |
if $*BINDVAL { | |
@@ -1295,6 +1331,41 @@ my class MASTCompilerInstance { | |
$*REGALLOC.release_register($valmast.result_reg, $res_kind); | |
} | |
$res_reg := $local; | |
+ } elsif $localref { | |
+ if $*BINDVAL { | |
+ nqp::die('Cannot bind to QAST::Var resolving to a localref'); | |
+ } | |
+ $res_kind := $*BLOCK.localref_kind($name); | |
+ $res_reg := $*REGALLOC.fresh_register($res_kind); | |
+ push_op(@ins, @decont_opnames[@kind_to_op_slot[$res_kind]], $res_reg, $localref); | |
+ } | |
+ else { | |
+ nqp::die("Cannot reference undeclared local '$name'"); | |
+ } | |
+ } | |
+ elsif $scope eq 'localref' { | |
+ my $localref := $*BLOCK.localref($name); | |
+ my $local := $*BLOCK.local($name); | |
+ if $localref { | |
+ $res_kind := $*BLOCK.localref_kind($name); | |
+ if $*BINDVAL { | |
+ my $valmast := self.as_mast_clear_bindval($*BINDVAL, :want($MVM_reg_obj)); | |
+ push_ilist(@ins, $valmast); | |
+ push_op(@ins, 'set', $localref, $valmast.result_reg); | |
+ $*REGALLOC.release_register($valmast.result_reg, $MVM_reg_obj); | |
+ } | |
+ $res_reg := $localref; | |
+ } | |
+ elsif $local { | |
+ if $*BINDVAL { | |
+ nqp::die('Cannot bind to a local resolving to a localref'); | |
+ } | |
+ my $res_kind := $*BLOCK.local_kind($name); | |
+ if $res_kind == $MVM_reg_obj { | |
+ nqp::die('Cannot take a reference to a non-native local'); | |
+ } | |
+ $res_reg := $*REGALLOC.fresh_register($MVM_reg_obj); | |
+ push_op(@ins, @localref_opnames[@kind_to_op_slot[$res_kind]], $res_reg, $local); | |
} | |
else { | |
nqp::die("Cannot reference undeclared local '$name'"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment