Skip to content

Instantly share code, notes, and snippets.

@timo
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timo/b68416dedda71b72a458 to your computer and use it in GitHub Desktop.
Save timo/b68416dedda71b72a458 to your computer and use it in GitHub Desktop.
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'
);
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