Skip to content

Commit

Permalink
initial implementation of localref X local access
Browse files Browse the repository at this point in the history
as well as three simple failing tests
  • Loading branch information
timo committed Jul 26, 2015
1 parent 16bee5d commit d125c71
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 3 deletions.
77 changes: 74 additions & 3 deletions src/vm/moar/QAST/QASTCompilerMAST.nqp
Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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} }
Expand Down Expand Up @@ -1148,6 +1173,12 @@ my class MASTCompilerInstance {
'getlexref_ns'
];

my @localref_opnames := [
'getregref_i',
'getregref_n',
'getregref_s'
];

my @decont_opnames := [
'decont_i',
'decont_n',
Expand Down Expand Up @@ -1222,8 +1253,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' {
Expand Down Expand Up @@ -1275,6 +1309,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 {
Expand All @@ -1284,6 +1320,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'");
Expand Down
98 changes: 98 additions & 0 deletions t/moar/02-qast-references.t
@@ -0,0 +1,98 @@
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);
nqp::setcontspec($class, 'native_ref', nqp::null());
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>, :returns(int) ),
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_n>,
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_s>,
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'
);

0 comments on commit d125c71

Please sign in to comment.