Commit 92f0112c by Than McIntosh Committed by Ian Lance Taylor

compiler: introduce size threshold for nil checks

    
    Add a new control variable to the Gogo class that stores the size
    threshold for nil checks. This value can be used to control the
    policy for deciding when a given deference operation needs a check and
    when it does not. A size threshold of -1 means that every potentially
    faulting dereference needs an explicit check (and branch to error
    call). A size threshold of K (where K > 0) means that if the size of
    the object being dereferenced is >= K, then we need a check.
    
    Reviewed-on: https://go-review.googlesource.com/80996

	* go-c.h (go_create_gogo_args): Add nil_check_size_threshold
	field.
	* go-lang.c (go_langhook_init): Set nil_check_size_threshold.

From-SVN: r255340
parent 19041dad
2017-12-01 Than McIntosh <thanm@google.com>
* go-c.h (go_create_gogo_args): Add nil_check_size_threshold
field.
* go-lang.c (go_langhook_init): Set nil_check_size_threshold.
2017-11-28 Jakub Jelinek <jakub@redhat.com> 2017-11-28 Jakub Jelinek <jakub@redhat.com>
* go-gcc.cc (Gcc_backend::switch_statement): Build SWITCH_EXPR using * go-gcc.cc (Gcc_backend::switch_statement): Build SWITCH_EXPR using
......
...@@ -47,6 +47,7 @@ struct go_create_gogo_args ...@@ -47,6 +47,7 @@ struct go_create_gogo_args
bool check_divide_overflow; bool check_divide_overflow;
bool compiling_runtime; bool compiling_runtime;
int debug_escape_level; int debug_escape_level;
int64_t nil_check_size_threshold;
}; };
extern void go_create_gogo (const struct go_create_gogo_args*); extern void go_create_gogo (const struct go_create_gogo_args*);
......
...@@ -112,6 +112,7 @@ go_langhook_init (void) ...@@ -112,6 +112,7 @@ go_langhook_init (void)
args.check_divide_overflow = go_check_divide_overflow; args.check_divide_overflow = go_check_divide_overflow;
args.compiling_runtime = go_compiling_runtime; args.compiling_runtime = go_compiling_runtime;
args.debug_escape_level = go_debug_escape_level; args.debug_escape_level = go_debug_escape_level;
args.nil_check_size_threshold = 4096;
args.linemap = go_get_linemap(); args.linemap = go_get_linemap();
args.backend = go_get_backend(); args.backend = go_get_backend();
go_create_gogo (&args); go_create_gogo (&args);
......
0d6b3abcbfe04949db947081651a503ceb12fe6e 8cd42a3e9e0e618bb09e67be73f7d2f2477a0faa
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -506,6 +506,23 @@ class Expression ...@@ -506,6 +506,23 @@ class Expression
static Expression* static Expression*
make_backend(Bexpression*, Type*, Location); make_backend(Bexpression*, Type*, Location);
enum Nil_check_classification
{
// Use the default policy for deciding if this deref needs a check.
NIL_CHECK_DEFAULT,
// An explicit check is required for this dereference operation.
NIL_CHECK_NEEDED,
// No check needed for this dereference operation.
NIL_CHECK_NOT_NEEDED,
// A type error or error construct was encountered when determining
// whether this deref needs an explicit check.
NIL_CHECK_ERROR_ENCOUNTERED
};
// Make a dereference expression.
static Expression*
make_dereference(Expression*, Nil_check_classification, Location);
// Return the expression classification. // Return the expression classification.
Expression_classification Expression_classification
classification() const classification() const
...@@ -1730,7 +1747,8 @@ class Unary_expression : public Expression ...@@ -1730,7 +1747,8 @@ class Unary_expression : public Expression
Unary_expression(Operator op, Expression* expr, Location location) Unary_expression(Operator op, Expression* expr, Location location)
: Expression(EXPRESSION_UNARY, location), : Expression(EXPRESSION_UNARY, location),
op_(op), escapes_(true), create_temp_(false), is_gc_root_(false), op_(op), escapes_(true), create_temp_(false), is_gc_root_(false),
is_slice_init_(false), expr_(expr), issue_nil_check_(false) is_slice_init_(false), expr_(expr),
issue_nil_check_(NIL_CHECK_DEFAULT)
{ } { }
// Return the operator. // Return the operator.
...@@ -1792,6 +1810,17 @@ class Unary_expression : public Expression ...@@ -1792,6 +1810,17 @@ class Unary_expression : public Expression
static Expression* static Expression*
do_import(Import*); do_import(Import*);
// Declare that this deref does or does not require an explicit nil check.
void
set_requires_nil_check(bool needed)
{
go_assert(this->op_ == OPERATOR_MULT);
if (needed)
this->issue_nil_check_ = NIL_CHECK_NEEDED;
else
this->issue_nil_check_ = NIL_CHECK_NOT_NEEDED;
}
protected: protected:
int int
do_traverse(Traverse* traverse) do_traverse(Traverse* traverse)
...@@ -1847,12 +1876,20 @@ class Unary_expression : public Expression ...@@ -1847,12 +1876,20 @@ class Unary_expression : public Expression
void void
do_issue_nil_check() do_issue_nil_check()
{ this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); } {
if (this->op_ == OPERATOR_MULT)
this->set_requires_nil_check(true);
}
private: private:
static bool static bool
base_is_static_initializer(Expression*); base_is_static_initializer(Expression*);
// Return a determination as to whether this dereference expression
// requires a nil check.
Nil_check_classification
requires_nil_check(Gogo*);
// The unary operator to apply. // The unary operator to apply.
Operator op_; Operator op_;
// Normally true. False if this is an address expression which does // Normally true. False if this is an address expression which does
...@@ -1874,7 +1911,7 @@ class Unary_expression : public Expression ...@@ -1874,7 +1911,7 @@ class Unary_expression : public Expression
Expression* expr_; Expression* expr_;
// Whether or not to issue a nil check for this expression if its address // Whether or not to issue a nil check for this expression if its address
// is being taken. // is being taken.
bool issue_nil_check_; Nil_check_classification issue_nil_check_;
}; };
// A binary expression. // A binary expression.
......
...@@ -41,6 +41,7 @@ go_create_gogo(const struct go_create_gogo_args* args) ...@@ -41,6 +41,7 @@ go_create_gogo(const struct go_create_gogo_args* args)
if (args->c_header != NULL) if (args->c_header != NULL)
::gogo->set_c_header(args->c_header); ::gogo->set_c_header(args->c_header);
::gogo->set_debug_escape_level(args->debug_escape_level); ::gogo->set_debug_escape_level(args->debug_escape_level);
::gogo->set_nil_check_size_threshold(args->nil_check_size_threshold);
} }
// Parse the input files. // Parse the input files.
......
...@@ -55,6 +55,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -55,6 +55,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
check_divide_overflow_(true), check_divide_overflow_(true),
compiling_runtime_(false), compiling_runtime_(false),
debug_escape_level_(0), debug_escape_level_(0),
nil_check_size_threshold_(4096),
verify_types_(), verify_types_(),
interface_types_(), interface_types_(),
specific_type_functions_(), specific_type_functions_(),
...@@ -5567,7 +5568,10 @@ Function::build(Gogo* gogo, Named_object* named_function) ...@@ -5567,7 +5568,10 @@ Function::build(Gogo* gogo, Named_object* named_function)
vars.push_back(bvar); vars.push_back(bvar);
Expression* parm_ref = Expression* parm_ref =
Expression::make_var_reference(parm_no, loc); Expression::make_var_reference(parm_no, loc);
parm_ref = Expression::make_unary(OPERATOR_MULT, parm_ref, loc); parm_ref =
Expression::make_dereference(parm_ref,
Expression::NIL_CHECK_DEFAULT,
loc);
if ((*p)->var_value()->is_in_heap()) if ((*p)->var_value()->is_in_heap())
parm_ref = Expression::make_heap_expression(parm_ref, loc); parm_ref = Expression::make_heap_expression(parm_ref, loc);
var_inits.push_back(parm_ref->get_backend(&context)); var_inits.push_back(parm_ref->get_backend(&context));
......
...@@ -318,6 +318,20 @@ class Gogo ...@@ -318,6 +318,20 @@ class Gogo
set_debug_escape_level(int level) set_debug_escape_level(int level)
{ this->debug_escape_level_ = level; } { this->debug_escape_level_ = level; }
// Return the size threshold used to determine whether to issue
// a nil-check for a given pointer dereference. A threshold of -1
// implies that all potentially faulting dereference ops should
// be nil-checked. A positive threshold of N implies that a deref
// of *P where P has size less than N doesn't need a nil check.
int64_t
nil_check_size_threshold() const
{ return this->nil_check_size_threshold_; }
// Set the nil-check size threshold, as described above.
void
set_nil_check_size_threshold(int64_t bytes)
{ this->nil_check_size_threshold_ = bytes; }
// Import a package. FILENAME is the file name argument, LOCAL_NAME // Import a package. FILENAME is the file name argument, LOCAL_NAME
// is the local name to give to the package. If LOCAL_NAME is empty // is the local name to give to the package. If LOCAL_NAME is empty
// the declarations are added to the global scope. // the declarations are added to the global scope.
...@@ -1025,6 +1039,8 @@ class Gogo ...@@ -1025,6 +1039,8 @@ class Gogo
// The level of escape analysis debug information to emit, from the // The level of escape analysis debug information to emit, from the
// -fgo-debug-escape option. // -fgo-debug-escape option.
int debug_escape_level_; int debug_escape_level_;
// Nil-check size threshhold.
int64_t nil_check_size_threshold_;
// A list of types to verify. // A list of types to verify.
std::vector<Type*> verify_types_; std::vector<Type*> verify_types_;
// A list of interface types defined while parsing. // A list of interface types defined while parsing.
......
...@@ -2745,14 +2745,17 @@ Parse::enclosing_var_reference(Named_object* in_function, Named_object* var, ...@@ -2745,14 +2745,17 @@ Parse::enclosing_var_reference(Named_object* in_function, Named_object* var,
Expression* closure_ref = Expression::make_var_reference(closure, Expression* closure_ref = Expression::make_var_reference(closure,
location); location);
closure_ref = Expression::make_unary(OPERATOR_MULT, closure_ref, location); closure_ref =
Expression::make_dereference(closure_ref,
Expression::NIL_CHECK_DEFAULT,
location);
// The closure structure holds pointers to the variables, so we need // The closure structure holds pointers to the variables, so we need
// to introduce an indirection. // to introduce an indirection.
Expression* e = Expression::make_field_reference(closure_ref, Expression* e = Expression::make_field_reference(closure_ref,
ins.first->index(), ins.first->index(),
location); location);
e = Expression::make_unary(OPERATOR_MULT, e, location); e = Expression::make_dereference(e, Expression::NIL_CHECK_DEFAULT, location);
return Expression::make_enclosing_var_reference(e, var, location); return Expression::make_enclosing_var_reference(e, var, location);
} }
......
...@@ -315,7 +315,8 @@ Variable_declaration_statement::do_get_backend(Translate_context* context) ...@@ -315,7 +315,8 @@ Variable_declaration_statement::do_get_backend(Translate_context* context)
if (binit != NULL) if (binit != NULL)
{ {
Expression* e = Expression::make_temporary_reference(temp, loc); Expression* e = Expression::make_temporary_reference(temp, loc);
e = Expression::make_unary(OPERATOR_MULT, e, loc); e = Expression::make_dereference(e, Expression::NIL_CHECK_NOT_NEEDED,
loc);
Bexpression* be = e->get_backend(context); Bexpression* be = e->get_backend(context);
set = context->backend()->assignment_statement(bfunction, be, binit, loc); set = context->backend()->assignment_statement(bfunction, be, binit, loc);
} }
...@@ -740,7 +741,9 @@ Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, ...@@ -740,7 +741,9 @@ Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
a1, a2, a3); a1, a2, a3);
Type* ptrval_type = Type::make_pointer_type(mt->val_type()); Type* ptrval_type = Type::make_pointer_type(mt->val_type());
call = Expression::make_cast(ptrval_type, call, loc); call = Expression::make_cast(ptrval_type, call, loc);
Expression* indir = Expression::make_unary(OPERATOR_MULT, call, loc); Expression* indir =
Expression::make_dereference(call, Expression::NIL_CHECK_NOT_NEEDED,
loc);
ref = Expression::make_temporary_reference(val_temp, loc); ref = Expression::make_temporary_reference(val_temp, loc);
b->add_statement(Statement::make_assignment(indir, ref, loc)); b->add_statement(Statement::make_assignment(indir, ref, loc));
...@@ -1292,7 +1295,8 @@ Tuple_map_assignment_statement::do_lower(Gogo* gogo, Named_object*, ...@@ -1292,7 +1295,8 @@ Tuple_map_assignment_statement::do_lower(Gogo* gogo, Named_object*,
// val = *val__ptr_temp // val = *val__ptr_temp
ref = Expression::make_temporary_reference(val_ptr_temp, loc); ref = Expression::make_temporary_reference(val_ptr_temp, loc);
Expression* ind = Expression::make_unary(OPERATOR_MULT, ref, loc); Expression* ind =
Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED, loc);
s = Statement::make_assignment(this->val_, ind, loc); s = Statement::make_assignment(this->val_, ind, loc);
b->add_statement(s); b->add_statement(s);
...@@ -2367,8 +2371,10 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) ...@@ -2367,8 +2371,10 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
// ones used in build_struct. // ones used in build_struct.
Expression* thunk_parameter = Expression::make_var_reference(named_parameter, Expression* thunk_parameter = Expression::make_var_reference(named_parameter,
location); location);
thunk_parameter = Expression::make_unary(OPERATOR_MULT, thunk_parameter, thunk_parameter =
location); Expression::make_dereference(thunk_parameter,
Expression::NIL_CHECK_NOT_NEEDED,
location);
Interface_field_reference_expression* interface_method = Interface_field_reference_expression* interface_method =
ce->fn()->interface_field_reference_expression(); ce->fn()->interface_field_reference_expression();
...@@ -2421,8 +2427,10 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) ...@@ -2421,8 +2427,10 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
{ {
Expression* thunk_param = Expression* thunk_param =
Expression::make_var_reference(named_parameter, location); Expression::make_var_reference(named_parameter, location);
thunk_param = thunk_param =
Expression::make_unary(OPERATOR_MULT, thunk_param, location); Expression::make_dereference(thunk_param,
Expression::NIL_CHECK_NOT_NEEDED,
location);
param = Expression::make_field_reference(thunk_param, param = Expression::make_field_reference(thunk_param,
next_index, next_index,
location); location);
...@@ -5837,7 +5845,8 @@ For_range_statement::lower_range_map(Gogo* gogo, ...@@ -5837,7 +5845,8 @@ For_range_statement::lower_range_map(Gogo* gogo,
Expression* lhs = Expression::make_temporary_reference(index_temp, loc); Expression* lhs = Expression::make_temporary_reference(index_temp, loc);
Expression* rhs = Expression::make_temporary_reference(hiter, loc); Expression* rhs = Expression::make_temporary_reference(hiter, loc);
rhs = Expression::make_field_reference(ref, 0, loc); rhs = Expression::make_field_reference(ref, 0, loc);
rhs = Expression::make_unary(OPERATOR_MULT, ref, loc); rhs = Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED,
loc);
Statement* set = Statement::make_assignment(lhs, rhs, loc); Statement* set = Statement::make_assignment(lhs, rhs, loc);
iter_init->add_statement(set); iter_init->add_statement(set);
...@@ -5846,7 +5855,8 @@ For_range_statement::lower_range_map(Gogo* gogo, ...@@ -5846,7 +5855,8 @@ For_range_statement::lower_range_map(Gogo* gogo,
lhs = Expression::make_temporary_reference(value_temp, loc); lhs = Expression::make_temporary_reference(value_temp, loc);
rhs = Expression::make_temporary_reference(hiter, loc); rhs = Expression::make_temporary_reference(hiter, loc);
rhs = Expression::make_field_reference(rhs, 1, loc); rhs = Expression::make_field_reference(rhs, 1, loc);
rhs = Expression::make_unary(OPERATOR_MULT, rhs, loc); rhs = Expression::make_dereference(rhs, Expression::NIL_CHECK_NOT_NEEDED,
loc);
set = Statement::make_assignment(lhs, rhs, loc); set = Statement::make_assignment(lhs, rhs, loc);
iter_init->add_statement(set); iter_init->add_statement(set);
} }
......
...@@ -2215,10 +2215,10 @@ Type::write_named_equal(Gogo* gogo, Named_type* name) ...@@ -2215,10 +2215,10 @@ Type::write_named_equal(Gogo* gogo, Named_type* name)
// Compare the values for equality. // Compare the values for equality.
Expression* t1 = Expression::make_temporary_reference(p1, bloc); Expression* t1 = Expression::make_temporary_reference(p1, bloc);
t1 = Expression::make_unary(OPERATOR_MULT, t1, bloc); t1 = Expression::make_dereference(t1, Expression::NIL_CHECK_NOT_NEEDED, bloc);
Expression* t2 = Expression::make_temporary_reference(p2, bloc); Expression* t2 = Expression::make_temporary_reference(p2, bloc);
t2 = Expression::make_unary(OPERATOR_MULT, t2, bloc); t2 = Expression::make_dereference(t2, Expression::NIL_CHECK_NOT_NEEDED, bloc);
Expression* cond = Expression::make_binary(OPERATOR_EQEQ, t1, t2, bloc); Expression* cond = Expression::make_binary(OPERATOR_EQEQ, t1, t2, bloc);
...@@ -5911,7 +5911,9 @@ Struct_type::field_reference_depth(Expression* struct_expr, ...@@ -5911,7 +5911,9 @@ Struct_type::field_reference_depth(Expression* struct_expr,
Expression* here = Expression::make_field_reference(struct_expr, i, Expression* here = Expression::make_field_reference(struct_expr, i,
location); location);
if (pf->type()->points_to() != NULL) if (pf->type()->points_to() != NULL)
here = Expression::make_unary(OPERATOR_MULT, here, location); here = Expression::make_dereference(here,
Expression::NIL_CHECK_DEFAULT,
location);
while (sub->expr() != NULL) while (sub->expr() != NULL)
{ {
sub = sub->expr()->deref()->field_reference_expression(); sub = sub->expr()->deref()->field_reference_expression();
...@@ -6342,11 +6344,13 @@ Struct_type::write_equal_function(Gogo* gogo, Named_type* name) ...@@ -6342,11 +6344,13 @@ Struct_type::write_equal_function(Gogo* gogo, Named_type* name)
// Compare one field in both P1 and P2. // Compare one field in both P1 and P2.
Expression* f1 = Expression::make_temporary_reference(p1, bloc); Expression* f1 = Expression::make_temporary_reference(p1, bloc);
f1 = Expression::make_unary(OPERATOR_MULT, f1, bloc); f1 = Expression::make_dereference(f1, Expression::NIL_CHECK_DEFAULT,
bloc);
f1 = Expression::make_field_reference(f1, field_index, bloc); f1 = Expression::make_field_reference(f1, field_index, bloc);
Expression* f2 = Expression::make_temporary_reference(p2, bloc); Expression* f2 = Expression::make_temporary_reference(p2, bloc);
f2 = Expression::make_unary(OPERATOR_MULT, f2, bloc); f2 = Expression::make_dereference(f2, Expression::NIL_CHECK_DEFAULT,
bloc);
f2 = Expression::make_field_reference(f2, field_index, bloc); f2 = Expression::make_field_reference(f2, field_index, bloc);
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, f1, f2, bloc); Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, f1, f2, bloc);
...@@ -7193,12 +7197,12 @@ Array_type::write_equal_function(Gogo* gogo, Named_type* name) ...@@ -7193,12 +7197,12 @@ Array_type::write_equal_function(Gogo* gogo, Named_type* name)
// Compare element in P1 and P2. // Compare element in P1 and P2.
Expression* e1 = Expression::make_temporary_reference(p1, bloc); Expression* e1 = Expression::make_temporary_reference(p1, bloc);
e1 = Expression::make_unary(OPERATOR_MULT, e1, bloc); e1 = Expression::make_dereference(e1, Expression::NIL_CHECK_DEFAULT, bloc);
ref = Expression::make_temporary_reference(index, bloc); ref = Expression::make_temporary_reference(index, bloc);
e1 = Expression::make_array_index(e1, ref, NULL, NULL, bloc); e1 = Expression::make_array_index(e1, ref, NULL, NULL, bloc);
Expression* e2 = Expression::make_temporary_reference(p2, bloc); Expression* e2 = Expression::make_temporary_reference(p2, bloc);
e2 = Expression::make_unary(OPERATOR_MULT, e2, bloc); e2 = Expression::make_dereference(e2, Expression::NIL_CHECK_DEFAULT, bloc);
ref = Expression::make_temporary_reference(index, bloc); ref = Expression::make_temporary_reference(index, bloc);
e2 = Expression::make_array_index(e2, ref, NULL, NULL, bloc); e2 = Expression::make_array_index(e2, ref, NULL, NULL, bloc);
...@@ -11219,7 +11223,8 @@ Type::apply_field_indexes(Expression* expr, ...@@ -11219,7 +11223,8 @@ Type::apply_field_indexes(Expression* expr,
if (expr->type()->struct_type() == NULL) if (expr->type()->struct_type() == NULL)
{ {
go_assert(expr->type()->points_to() != NULL); go_assert(expr->type()->points_to() != NULL);
expr = Expression::make_unary(OPERATOR_MULT, expr, location); expr = Expression::make_dereference(expr, Expression::NIL_CHECK_DEFAULT,
location);
go_assert(expr->type()->struct_type() == stype); go_assert(expr->type()->struct_type() == stype);
} }
return Expression::make_field_reference(expr, field_indexes->field_index, return Expression::make_field_reference(expr, field_indexes->field_index,
...@@ -11323,7 +11328,8 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, ...@@ -11323,7 +11328,8 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr,
&& type->points_to() != NULL && type->points_to() != NULL
&& type->points_to()->points_to() != NULL) && type->points_to()->points_to() != NULL)
{ {
expr = Expression::make_unary(OPERATOR_MULT, expr, location); expr = Expression::make_dereference(expr, Expression::NIL_CHECK_DEFAULT,
location);
type = type->points_to(); type = type->points_to();
if (type->deref()->is_error_type()) if (type->deref()->is_error_type())
return Expression::make_error(location); return Expression::make_error(location);
...@@ -11356,8 +11362,9 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, ...@@ -11356,8 +11362,9 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr,
return Expression::make_error(location); return Expression::make_error(location);
} }
go_assert(type->points_to() != NULL); go_assert(type->points_to() != NULL);
expr = Expression::make_unary(OPERATOR_MULT, expr, expr = Expression::make_dereference(expr,
location); Expression::NIL_CHECK_DEFAULT,
location);
go_assert(expr->type()->struct_type() == st); go_assert(expr->type()->struct_type() == st);
} }
ret = st->field_reference(expr, name, location); ret = st->field_reference(expr, name, location);
......
...@@ -417,7 +417,8 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, ...@@ -417,7 +417,8 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing,
rhs = Expression::make_temporary_reference(rhs_temp, loc); rhs = Expression::make_temporary_reference(rhs_temp, loc);
} }
Expression* indir = Expression::make_unary(OPERATOR_MULT, lhs, loc); Expression* indir =
Expression::make_dereference(lhs, Expression::NIL_CHECK_DEFAULT, loc);
Statement* assign = Statement::make_assignment(indir, rhs, loc); Statement* assign = Statement::make_assignment(indir, rhs, loc);
lhs = Expression::make_temporary_reference(lhs_temp, loc); lhs = Expression::make_temporary_reference(lhs_temp, loc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment