Skip to content

Commit

Permalink
properly emit function value vs. function call #2024-func-call-update
Browse files Browse the repository at this point in the history
  • Loading branch information
gewang committed Nov 27, 2024
1 parent bade6d2 commit 71c9839
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 23 deletions.
5 changes: 5 additions & 0 deletions src/core/chuck_absyn.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ struct a_Exp_
t_CKTYPE cast_to;
t_CKUINT emit_var; // 1 = emit var, 2 = emit var and value

// assuming this Exp is a func, this will properly
// emit in preparation to be called | 1.5.4.3 (ge) added
// #2024-func-call-update
t_CKBOOL emit_as_funccall;

union
{
struct a_Exp_Binary_ binary;
Expand Down
76 changes: 62 additions & 14 deletions src/core/chuck_emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ t_CKBOOL emit_engine_emit_spork( Chuck_Emitter * emit, a_Exp_Func_Call exp );
t_CKBOOL emit_engine_emit_cast( Chuck_Emitter * emit, Chuck_Type * to, Chuck_Type * from, uint32_t where );
t_CKBOOL emit_engine_emit_symbol( Chuck_Emitter * emit, S_Symbol symbol,
Chuck_Value * v, t_CKBOOL emit_var,
t_CKUINT line, t_CKUINT where );
t_CKUINT line, t_CKUINT where, a_Exp_Primary exp );
Chuck_Instr_Stmt_Start * emit_engine_track_stmt_refs_start( Chuck_Emitter * emit, a_Stmt stmt );
void emit_engine_track_stmt_refs_cleanup( Chuck_Emitter * emit, Chuck_Instr_Stmt_Start * start );
// disabled until further notice (added 1.3.0.0)
Expand Down Expand Up @@ -275,7 +275,7 @@ Chuck_VM_Code * emit_engine_emit_prog( Chuck_Emitter * emit, a_Program prog,
emit->pop_scope();
// append end of code
emit->append( new Chuck_Instr_EOC );
// add code str to whichever instruction began this section | 1.5.4.2 (ge) added
// add code str to whichever instruction began this section | 1.5.4.3 (ge) added
emit->code->code[index]->prepend_codestr( "/* end of code */" );

// make sure
Expand Down Expand Up @@ -2050,12 +2050,19 @@ t_CKBOOL emit_engine_emit_exp_binary( Chuck_Emitter * emit, a_Exp_Binary binary
// whether to track object references on stack (added 1.3.0.2)
t_CKBOOL doRefLeft = FALSE;
t_CKBOOL doRefRight = FALSE;

// check to see if this is a function call (added 1.3.0.2)
// i.e., does LHS => RHS resolve to function call RHS(LHS)?
// added ae_op_chuck and make sure not null, latter has type-equivalence with object types in certain contexts | 1.5.1.7
if( binary->op == ae_op_chuck && isa( binary->rhs->type, emit->env->ckt_function ) && !isnull( emit->env, binary->rhs->type ) )
// if( binary->op == ae_op_chuck && isa( binary->rhs->type, emit->env->ckt_function ) && !isnull( emit->env, binary->rhs->type ) )
if( type_engine_binary_is_func_call( emit->env, binary->op, binary->lhs, binary->rhs ) )
{
// take care of objects in terms of reference counting
doRefLeft = TRUE;

// need to know here so the RHS can properly emit
// 1.5.4.3 (ge) added as part of #2024-func-call-update
binary->rhs->emit_as_funccall = TRUE;
}
// check operator overload | 1.5.1.5 (ge)
t_CKBOOL op_overload = (binary->ck_overload_func != NULL);
Expand Down Expand Up @@ -3158,6 +3165,9 @@ t_CKBOOL emit_engine_emit_op_overload_postfix( Chuck_Emitter * emit, a_Exp_Postf
//-----------------------------------------------------------------------------
t_CKBOOL emit_engine_emit_op_chuck( Chuck_Emitter * emit, a_Exp lhs, a_Exp rhs, a_Exp_Binary binary )
{
// consistency check | 1.5.4.3 (ge) added
assert( binary->op == ae_op_chuck );

// any implicit cast happens before this
Chuck_Type * left = lhs->cast_to ? lhs->cast_to : lhs->type;
Chuck_Type * right = rhs->cast_to ? rhs->cast_to : rhs->type;
Expand Down Expand Up @@ -3243,7 +3253,8 @@ t_CKBOOL emit_engine_emit_op_chuck( Chuck_Emitter * emit, a_Exp lhs, a_Exp rhs,

// func call
// make sure not 'null' which also looks like any object | 1.5.1.7
if( isa( right, emit->env->ckt_function ) && !isnull(emit->env,right) )
// if( isa( right, emit->env->ckt_function ) && !isnull(emit->env,right) )
if( type_engine_binary_is_func_call( emit->env, binary->op, lhs, rhs ) )
{
assert( binary->ck_func != NULL );

Expand Down Expand Up @@ -3740,7 +3751,7 @@ t_CKBOOL emit_engine_emit_exp_primary( Chuck_Emitter * emit, a_Exp_Primary exp )
{
// emit the symbol
return emit_engine_emit_symbol(
emit, exp->var, exp->value, exp->self->emit_var, exp->line, exp->where );
emit, exp->var, exp->value, exp->self->emit_var, exp->line, exp->where, exp );
}
break;

Expand Down Expand Up @@ -4282,6 +4293,10 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit,
return FALSE;
}

// need to know here so the func can properly emit
// 1.5.4.3 (ge) added as part of #2024-func-call-update
func_call->func->emit_as_funccall = TRUE;

// emit func
if( !emit_engine_emit_exp( emit, func_call->func ) )
{
Expand All @@ -4296,7 +4311,7 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit,
// additional func call options | 1.5.4.2 (ge) added
Chuck_FuncCall_Options options;

// in the case of member func calls
// check if func is a dot_member
if( func_call->func->s_type == ae_exp_dot_member )
{
// if possible, get more accurate code position
Expand Down Expand Up @@ -4620,10 +4635,15 @@ t_CKBOOL emit_engine_emit_exp_dot_member_special( Chuck_Emitter * emit,
emit->append( new Chuck_Instr_Reg_Transmute_Value_To_Pointer( t_base->size ) );
}

// dup the base pointer ('this' pointer as argument -- special case primitive)
// as of 1.5.4.2 this is emitted for both literals and variables
// #special-primitive-member-func-from-literal
emit->append( new Chuck_Instr_Reg_Dup_Last );
// check if we are part of a function call vs. function as value
// 1.5.4.3 (ge) added as part of #2024-func-call-update
if( member->self->emit_as_funccall )
{
// dup the base pointer ('this' pointer as argument -- special case primitive)
// as of 1.5.4.2 this is emitted for both literals and variables
// #special-primitive-member-func-from-literal
emit->append( new Chuck_Instr_Reg_Dup_Last );
}

// find the offset for virtual table
offset = func->vt_index;
Expand Down Expand Up @@ -4700,8 +4720,13 @@ t_CKBOOL emit_engine_emit_exp_dot_member( Chuck_Emitter * emit,
{
// emit the base (TODO: return on error?)
emit_engine_emit_exp( emit, member->base );
// dup the base pointer ('this' pointer as argument)
emit->append( new Chuck_Instr_Reg_Dup_Last );
// check if we are part of a function call vs. function as value
// 1.5.4.3 (ge) added as part of #2024-func-call-update
if( member->self->emit_as_funccall )
{
// dup the base pointer ('this' pointer as argument)
emit->append( new Chuck_Instr_Reg_Dup_Last );
}
// find the offset for virtual table
offset = func->vt_index;
// emit the function
Expand All @@ -4712,6 +4737,13 @@ t_CKBOOL emit_engine_emit_exp_dot_member( Chuck_Emitter * emit,
{
// emit the type
emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)t_base ) );
// check if we are part of a function call vs. function as value
// 1.5.4.3 (ge) added as part of #2024-func-call-update
if( member->self->emit_as_funccall )
{
// dup the base pointer ('this' pointer as argument)
emit->append( new Chuck_Instr_Reg_Dup_Last );
}
// emit the static function
emit->append( new Chuck_Instr_Dot_Static_Func( func ) );
}
Expand Down Expand Up @@ -4769,6 +4801,14 @@ t_CKBOOL emit_engine_emit_exp_dot_member( Chuck_Emitter * emit,
{
// emit the type - spencer
emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)t_base ) );
// if part of a func call
// 1.5.4.3 (ge) added as part of #2024-func-call-update
if( member->self->emit_as_funccall )
{
// dupe the pointer, as Chuck_Instr_Dot_Static_Func will consume it
// 1.5.4.3 (ge) added as part of #2024-func-call-update
emit->append( new Chuck_Instr_Reg_Dup_Last );
}
// get the func | 1.4.1.0 (ge) added looking in parent
value = type_engine_find_value( t_base, member->xid );
// get the function reference
Expand Down Expand Up @@ -5861,6 +5901,10 @@ t_CKBOOL emit_engine_emit_spork( Chuck_Emitter * emit, a_Exp_Func_Call exp )
if( !emit_engine_emit_func_args( emit, exp ) )
return FALSE;

// need to know here so the func can properly emit
// 1.5.4.3 (ge) added as part of #2024-func-call-update
exp->func->emit_as_funccall = TRUE;

// emit func pointer on sporker shred
if( !emit_engine_emit_exp( emit, exp->func ) )
{
Expand Down Expand Up @@ -5953,11 +5997,11 @@ t_CKBOOL emit_engine_emit_spork( Chuck_Emitter * emit, a_Exp_Func_Call exp )

//-----------------------------------------------------------------------------
// name: emit_engine_emit_symbol()
// desc: ...
// desc: emit a symbol into code
//-----------------------------------------------------------------------------
t_CKBOOL emit_engine_emit_symbol( Chuck_Emitter * emit, S_Symbol symbol,
Chuck_Value * v, t_CKBOOL emit_var,
t_CKUINT line, t_CKUINT where )
t_CKUINT line, t_CKUINT where, a_Exp_Primary exp )
{
// look up the value
// Chuck_Value * v = emit->env->curr->lookup_value( symbol, TRUE );
Expand Down Expand Up @@ -6040,6 +6084,10 @@ t_CKBOOL emit_engine_emit_symbol( Chuck_Emitter * emit, S_Symbol symbol,
CK_SAFE_ADD_REF( dot->dot_member.t_base ); // 1.5.1.3
dot->emit_var = emit_var;

// propagate whether this should be emitted as a func call (vs. a function value only)
// 1.5.4.3 (ge) added as part of #2024-func-call-update
dot->emit_as_funccall = exp->self->emit_as_funccall;

// emit it
if( !emit_engine_emit_exp_dot_member( emit, &dot->dot_member ) )
{
Expand Down
9 changes: 7 additions & 2 deletions src/core/chuck_instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7776,7 +7776,11 @@ void Chuck_Instr_Dot_Static_Func::execute( Chuck_VM * vm, Chuck_VM_Shred * shred

// 1.4.1.0 (ge): leave the base type on the operand stack
// commented out: pop the type pointer
// pop_( sp, 1 );
// 1.5.4.3 (ge): uncommented, remove base pointer, consistent with
// other dot-member function emission; this helps cleaning up the
// stack, depending on whether this is part of a function value emission
// only, or going to be used as a function call #2024-func-call-update
pop_( sp, 1 );

// push the address
push_( sp, (t_CKUINT)(m_func) );
Expand Down Expand Up @@ -9570,7 +9574,8 @@ void Chuck_Instr_Gack::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
if( *(sp) == 0 )
CK_FPRINTF_STDERR( "null " );
else
CK_FPRINTF_STDERR( "0x%lx (refcount=%d) ", *(sp), obj->m_ref_count );
CK_FPRINTF_STDERR( "0x%lx :(%s|refcount=%d)\n", *(sp), type->c_name(), obj->m_ref_count );
// CK_FPRINTF_STDERR( "0x%lx (refcount=%d) ", *(sp), obj->m_ref_count );
}
else
{
Expand Down
9 changes: 7 additions & 2 deletions src/core/chuck_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,15 @@ t_CKBOOL type_engine_scan0_class_def( Chuck_Env * env, a_Class_Def class_def )
if( the_class->is_public )
{ the_class->nspc->parent = env->context->nspc; }
else { the_class->nspc->parent = env->curr; }
the_class->func = NULL;

// 1.5.4.3 (ge) commented out...type2func_bridge already init to NULL
// #2024-func-call-update
// the_class->type2func_bridge = NULL;

// 1.5.0.5 (ge) commented out; the AST is cleaned up after every compilation;
// would need to make deep copy if want to keep around
// the_class->def = class_def;

// add code
the_class->nspc->pre_ctor = new Chuck_VM_Code;
CK_SAFE_ADD_REF( the_class->nspc->pre_ctor );
Expand Down Expand Up @@ -3085,7 +3090,7 @@ t_CKBOOL type_engine_scan2_func_def( Chuck_Env * env, a_Func_Def f )
type->base_name = "[function]";
type->parent = env->ckt_function; // TODO: reference count the parent
type->size = sizeof(void *);
type->func = func;
type->type2func_bridge = func; // TODO: consider ref count

// make new value, with potential overloaded name
value = env->context->new_Chuck_Value( type, func_name );
Expand Down
28 changes: 24 additions & 4 deletions src/core/chuck_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2737,7 +2737,8 @@ t_CKTYPE type_engine_check_op_chuck( Chuck_Env * env, a_Exp lhs, a_Exp rhs,
t_CKTYPE left = lhs->type, right = rhs->type;

// chuck to function
if( !isnull(env, right) && isa( right, env->ckt_function ) )
// if( !isnull(env, right) && isa( right, env->ckt_function ) )
if( type_engine_binary_is_func_call( env, ae_op_chuck, lhs, rhs ) )
{
// treat this function call
return type_engine_check_exp_func_call( env, rhs, lhs, binary->ck_func, binary->where );
Expand Down Expand Up @@ -4737,7 +4738,7 @@ t_CKTYPE type_engine_check_exp_func_call( Chuck_Env * env, a_Exp exp_func, a_Exp
}

// copy the func
up = f->func;
up = f->type2func_bridge;

// check the arguments
if( args )
Expand Down Expand Up @@ -8962,6 +8963,25 @@ t_CKBOOL type_engine_is_base_static( Chuck_Env * env, Chuck_Type * baseType )



//-----------------------------------------------------------------------------
// name: type_engine_binary_is_func_call() | 1.5.4.3 (ge)
// desc: check whether a binary expression is a function call
// i.e., LHS => RHS same as RHS(LHS)
//-----------------------------------------------------------------------------
t_CKBOOL type_engine_binary_is_func_call( Chuck_Env * env, ae_Operator op, a_Exp lhs, a_Exp rhs )
{
// get types
t_CKTYPE right = rhs->type;

// check LHS => RHS && RHS is a function (and not null)...
return op == ae_op_chuck
&& isa( right, env->ckt_function )
&& !isnull(env,right);
}




static const char * g_howmuch[] = { "ALL", "IMPORT_ONLY", "ALL_EXCEPT_IMPORT" };
//-----------------------------------------------------------------------------
// name: howmuch2str()
Expand Down Expand Up @@ -9744,7 +9764,7 @@ Chuck_Type::Chuck_Type( Chuck_Env * env, te_Type _id, const std::string & _n,
array_depth = 0;
obj_size = 0;
nspc = NULL;
func = NULL; /* def = NULL; */
type2func_bridge = NULL; /* def = NULL; */
is_public = FALSE;
is_copy = FALSE;
ugen_info = NULL;
Expand Down Expand Up @@ -9836,7 +9856,7 @@ const Chuck_Type & Chuck_Type::operator =( const Chuck_Type & rhs )
this->is_copy = TRUE;
this->array_depth = rhs.array_depth;
this->array_type = rhs.array_type; CK_SAFE_ADD_REF(this->array_type);
this->func = rhs.func; CK_SAFE_ADD_REF(this->func);
this->type2func_bridge = rhs.type2func_bridge; CK_SAFE_ADD_REF(this->type2func_bridge);
this->nspc = rhs.nspc; CK_SAFE_ADD_REF(this->nspc);
// this->owner = rhs.owner; CK_SAFE_ADD_REF(this->owner);

Expand Down
3 changes: 2 additions & 1 deletion src/core/chuck_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ struct Chuck_Type : public Chuck_Object
// type info
Chuck_Namespace * nspc;
// func info
Chuck_Func * func;
Chuck_Func * type2func_bridge;
// ugen
Chuck_UGen_Info * ugen_info;
// is public class | 1.5.4.0 (ge) added
Expand Down Expand Up @@ -1402,6 +1402,7 @@ t_CKBOOL type_engine_check_const( Chuck_Env * env, a_Exp e, int pos ); // TODO
t_CKBOOL type_engine_compat_func( a_Func_Def lhs, a_Func_Def rhs, int pos, std::string & err, t_CKBOOL print = TRUE );
t_CKBOOL type_engine_get_deprecate( Chuck_Env * env, const std::string & from, std::string & to );
t_CKBOOL type_engine_is_base_static( Chuck_Env * env, Chuck_Type * baseType ); // 1.5.0.0 (ge) added
t_CKBOOL type_engine_binary_is_func_call( Chuck_Env * env, ae_Operator op, a_Exp lhs, a_Exp rhs ); // 1.5.4.3 (ge) added
Chuck_Type * type_engine_find_common_anc( Chuck_Type * lhs, Chuck_Type * rhs );
Chuck_Type * type_engine_find_type( Chuck_Env * env, a_Id_List path );
Chuck_Type * type_engine_find_type( Chuck_Env * env, const std::string & name ); // 1.5.0.0 (ge) added
Expand Down
16 changes: 16 additions & 0 deletions src/test/04-Stress/func-call-1.ck
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// #2024-func-call-update verification

// test custom non-class function
fun void foo( int a ) { }

// call it
foo(1);
// get the function as value only
foo;

// check for stack overflow
repeat(100000) foo(2);
repeat(100000) foo;

// if we get here we are ok
<<< "success" >>>;
12 changes: 12 additions & 0 deletions src/test/04-Stress/func-call-10-spork-s.ck
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// #2024-func-call-update verification
// test spork static function from within a class
class Foo
{
static int N;
fun static void foo() { N++; spork ~ bar(); me.yield(); }
fun static void bar() { if( N >= 10000 ) <<< "success" >>>; }
}

0 => Foo.N;
// test for stack overflow
repeat(10000) Foo.foo();
11 changes: 11 additions & 0 deletions src/test/04-Stress/func-call-2-static.ck
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// #2024-func-call-update verification

// test static function
Machine.refcount;

// check for stack overflow
repeat(1000000)
Machine.refcount;

// if we get here, ok
<<< "success" >>>;
16 changes: 16 additions & 0 deletions src/test/04-Stress/func-call-3-member.ck
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// #2024-func-call-update verification

// test member function
SinOsc osc;

// emit as value without call
osc.gain;

// test for stack overflow
repeat(1000000) osc.gain;

// this might yield a compiler later on, until then...
repeat(1000000) osc.gain == "hello";

// if we get here, ok
<<< "success" >>>;
Loading

0 comments on commit 71c9839

Please sign in to comment.