[M3devel] update and static link stuff...

Jay K jay.krell at cornell.edu
Wed Sep 19 03:28:23 CEST 2012


latest debugging/exploration...

RTLinker.m3 has some indirect call (function call through a pointer).Here are two of them:

PROCEDURE FixImports (m: RT0.ModulePtr) =  VAR imp: RT0.ImportPtr;  BEGIN    IF (m = NIL) THEN RETURN; END;    TraceModule("FixImports: ", m);    imp := m.imports;    WHILE (imp # NIL) DO      IF (imp.import = NIL) THEN  imp.import := imp.binder (0);  END; (* line 83 *)      imp := imp.next;    END;  END FixImports;

PROCEDURE AddUnit (b: RT0.Binder) =  VAR m: RT0.ModulePtr;  BEGIN    IF (b = NIL) THEN RETURN END;    m := b(0);  (* line 122 *)    IF (m = NIL) THEN RETURN END;    AddUnitI(m);  END AddUnit;

m3front sometimes "knows" that an indirect call cannot be to a closure,so sometimes it does "static link stuff", sometimes not, UserProc.m3:

    ELSIF CouldBeClosure (proc) THEN      ce.tmp := GenClosureCall (p_temp, cg_result, p_type, callConv);      CG.Free (p_temp);    ELSE      CG.Push (p_temp);      CG.Gen_Call_indirect (cg_result, callConv);      ce.tmp := Marker.EmitExceptionTest (p_type, need_value := TRUE);      CG.Free (p_temp);    END;


PROCEDURE GenClosureCall (p_temp: CG.Val;  result: CG.Type;                          sig: Type.T;  cc: CG.CallingConvention): CG.Val =  VAR skip := CG.Next_label ();  BEGIN    CG.If_closure (p_temp, CG.No_label, skip, CG.Maybe);    CG.Push (p_temp);    CG.Closure_frame ();    CG.Pop_static_link ();   (* *** *)    CG.Push (p_temp);    CG.Closure_proc ();    CG.Store_temp (p_temp);    CG.Set_label (skip);    CG.Push (p_temp);    CG.Gen_Call_indirect (result, cc);    RETURN Marker.EmitExceptionTest (sig, need_value := TRUE);  END GenClosureCall;

>From the two examples and guessing at what the code means, I guesswe can't "store closures", like in globals or record fields.They can be parameters and locals.And that is how the compiler knows some things can't be closures.? 
Aside:The backends are relatively untyped.There is a lot of type information available, but isn't really used."Following that idea.." whenever my C backend calls through a function pointer,it casts to a function pointer that can accept any parameters:

#ifdef __cplusplus#define M3_DOTDOTDOT ...#else#define M3_DOTDOTDOT#endif

(recall that in C void Foo(void) is a function accepts no parameters, but void Foo()is a function that accepts anything; if you do use "..." in C, gcc complains about thelack of any preceding parameters.)

 (((*)(M3_DOTDOTDOT))function_pointer)(parameters any number of them)   so, now, let's look at those indirect calls in the generated C (slightly simplified):

This first one can't be a closure:

 /* line 83 "../src/runtime/common/RTLinker.m3" */ (*(volatile ADDRESS*)&L_7)=(ADDRESS)(((ADDRESS)(*(volatile ADDRESS*)(4+(ADDRESS)*(volatile ADDRESS*)&imp)))); /* start_call_indirect */ if(!(*(volatile ADDRESS*)&L_7))M_RTLinker_CRASH(2660); /* call_indirect */  /* declare_temp => declare_local */  /* declare_local ADDRESS L_10 */ {ADDRESS L_9 M3_INIT; /* store */ (*(volatile ADDRESS*)&L_9)=(ADDRESS)(((ADDRESS)(((ADDRESS (__stdcall*)(M3_DOTDOTDOT))*(volatile ADDRESS*)&L_7)(((INT32)(((INT32)M3_INT32(0)))))))); /* line 83 "../src/runtime/common/RTLinker.m3" */  /* load */ 


and this one can be, you can see the telltale check for -1, the closure marker:


 /* line 122 "../src/runtime/common/RTLinker.m3" */ 
if((((INT32)(*(volatile INT32*)*(volatile ADDRESS*)&L_13)))!=(((INT32)(((INT32)M3_INT32(-1))))))goto L12; /* set_label */ L13:; /* load */  /* load_indirect */  /* declare_temp => declare_local */  /* declare_local ADDRESS L_16 */ {ADDRESS L_15 M3_INIT; /* pop_static_link */  /* store */ (*(volatile ADDRESS*)&L_15)=(ADDRESS)(((ADDRESS)(*(volatile ADDRESS*)(8+(ADDRESS)*(volatile ADDRESS*)&L_13)))); /* load */  /* load_indirect */  /* store */ (*(volatile ADDRESS*)&L_13)=(ADDRESS)(((ADDRESS)(*(volatile ADDRESS*)(4+(ADDRESS)*(volatile ADDRESS*)&L_13)))); /* line 122 "../src/runtime/common/RTLinker.m3" */  /* set_label */ L12:; /* load */  /* check_nil */ if(!(*(volatile ADDRESS*)&L_13))M_RTLinker_CRASH(3908); /* call_indirect */  /* free_temp */  /* declare_temp => declare_local */  /* declare_local ADDRESS L_18 */ {ADDRESS L_17 M3_INIT; /* store */ (*(volatile ADDRESS*)&L_17)=(ADDRESS)(((ADDRESS)(((ADDRESS (__stdcall*)(M3_DOTDOTDOT))*(volatile ADDRESS*)&L_13)(L_15,((INT32)(((INT32)M3_INT32(0))))))));



These are actually both calls to "binders", which take one parameter, integer "mode".

They are both via pointers.

But the compiler thinks the second one might be a closure.So we get pop_static_link. Following the pattern of M3x86.m3, I therefore pass the static link, unconditionally.

This is not right!

For now I'm going to try putting the static link LAST in parameter lists, instead of first.

I expect that will work.Thanks to printf! :)(on all but one implementation I know of, printf("", 1, 2, 3) is safe and predictable -- you can pass any number of extra parameters to a function and they will be ignored; the stack will not become imbalanced. Anyone with a broad knowledge of calling conventions confirm?)

The right fix is to use more type information, possibly that which I'm already given.That is going to require multiple passes and a somewhat more sophisticated in-memorystate. Currently I have just a bunch of strings, occasionally an array of strings.Not all the data is recieved is received in an ideal order.

i.e. This should all work, pretty well, but it kind of needs a substantial "rewrite".


 - Jay
 		 	   		  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20120919/d9b4fec9/attachment-0001.html>


More information about the M3devel mailing list