home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
sa104os2.zip
/
SATHR104.ZIP
/
SATHER
/
COMPILER
/
BE.SA
< prev
next >
Wrap
Text File
|
1995-02-14
|
95KB
|
2,808 lines
-- Copyright (C) International Computer Science Institute, 1994. COPYRIGHT --
-- NOTICE: This code is provided "AS IS" WITHOUT ANY WARRANTY and is subject --
-- to the terms of the SATHER LIBRARY GENERAL PUBLIC LICENSE contained in --
-- the file "Doc/License" of the Sather distribution. The license is also --
-- available from ICSI, 1947 Center St., Suite 600, Berkeley CA 94704, USA. --
--------> Please email comments to "sather-bugs@icsi.berkeley.edu". <----------
class BE is
-- back-end C generation from an AM structure.
-- These are options that may be set outside BE.
attr indent:INT; -- current level of indentation
attr prog:PROG; -- The program's PROG object
-- These are private state needed for the translation.
private attr manglemap:FMAP{$OB,STR};
-- mapping from Sather objects to C strings
private attr mangleset:FSET{STR};
-- C strings which have already been emitted
private attr forbidden:FSET{STR};
-- Strings which may not be used as identifiers
private attr counter:INT;
-- Counter used for generating unique strings
private attr local_counter:INT;
-- For making unique local declarations
private attr state_counter:INT;
-- for yield goto's
private attr built_in_routines:FMAP{STR,TUP{STR,STR}};
-- Compiler-known functions and their associated C name
-- (Two versions, one w/o checking and one with.)
private attr built_in_iters:FMAP{STR,ITER_INLINE};
-- Compiler-known iters
private attr built_in_classes:FSET{$TP};
-- Compiler-known classes
private attr special_externs:FMAP{STR,STR};
-- external routines that need special prototypes
private attr typedefs_h, sather_h, decs_h,
globals_h, tags_h, strings_h, makefile:FILE;
private attr code_c_count:INT;
private attr code_c, globals_c:FILE;
-- The header and C files.
private attr code_dir:STR;
-- The directory all this happens in
private attr tags:FMAP{$TP,INT};
-- Integers associated with classes
private attr main_sig:SIG;
-- SIG corresponding to main
private attr current_function:SIG;
-- SIG of ccurrent function being translated
private attr current_function_str:STR;
-- Name of current function being translated
private attr current_self:STR;
-- expr for self
private attr saw_outer_return:BOOL;
-- was a return at outer level seen in this routine?
private attr chk_pre, chk_post, chk_invariant, chk_assert,
chk_arith, chk_bounds, chk_void, chk_when,
chk_destroy, chk_return:BOOL;
-- whether different checks are on for the current function
private attr nested_its:FLIST{AM_ITER_CALL_EXPR};
-- Stack of iter calls
private attr current_loop:STR;
-- label to goto to at end of loop
private attr abstract_routs:FLIST{AM_ROUT_DEF};
-- List of abstract routines to make dispatch tables for
private attr bnd_rout_creates:FLIST{AM_BND_CREATE_EXPR};
-- List of bound routine stubs to generate
private attr routine_code:FSTR;
-- code waiting to be emitted
private attr str_count:INT;
-- number of STR constants emitted (for making their id)
private attr inlined_sigs:FMAP{SIG,AM_ROUT_DEF};
-- routines that weren't generated because they were inlined
private attr routine_count:INT;
-- count of emitted routines, reset for each new file
private attr inlined_iter_count:INT;
-- count of how many iters got inlined
create(p:PROG):SAME is
res::=new;
res.prog:=p;
res.manglemap:=#;
res.mangleset:=#;
res.forbidden:=#;
res.special_externs:=#;
res.counter:=1;
res.built_in_routines:=#;
res.built_in_iters:=#;
res.built_in_classes:=#;
res.abstract_routs:=#;
res.bnd_rout_creates:=#;
res.str_count:=1;
res.inlined_sigs:=#;
res.insert_forbidden_names;
res.insert_built_in_routines;
res.insert_built_in_iters;
res.insert_built_in_classes;
res.insert_special_externs;
return res;
end;
init is
-- initialization should occur after the layouts and sigs have been constructed
indent:=0;
-- code_dir:=prog.options.executable+".code"; -- NLP
code_dir:=prog.options.executable+".cod"; -- NLP
FILE::create_directory(code_dir);
new_c_file;
fn::=code_dir+'/'+"typedefs.h";
typedefs_h:=FILE::open_for_write(fn);
if typedefs_h.error then barf("Couldn't open "+fn); end;
fn:=code_dir+'/'+"sather.h";
sather_h:=FILE::open_for_write(fn);
if sather_h.error then barf("Couldn't open "+fn); end;
fn:=code_dir+'/'+"decs.h";
decs_h:=FILE::open_for_write(fn);
if decs_h.error then barf("Couldn't open "+fn); end;
fn:=code_dir+'/'+"globals.h";
globals_h:=FILE::open_for_write(fn);
if globals_h.error then barf("Couldn't open "+fn); end;
fn:=code_dir+'/'+"globals.c";
globals_c:=FILE::open_for_write(fn);
if globals_c.error then barf("Couldn't open "+fn); end;
globals_c+"#include \"sather.h\"\n";
fn:=code_dir+'/'+"tags.h";
tags_h:=FILE::open_for_write(fn);
if tags_h.error then barf("Couldn't open "+fn); end;
fn:=code_dir+'/'+"strings.h";
strings_h:=FILE::open_for_write(fn);
if strings_h.error then barf("Couldn't open "+fn); end;
fn:=code_dir+'/'+"Makefile";
makefile:=FILE::open_for_write(fn);
if makefile.error then barf("Couldn't open "+fn); end;
if prog.options.deterministic then
sather_h+"#define DETERMINISTIC\n";
end;
comp_home::=prog.options.home;
if comp_home=".." then comp_home:="../.."; end; -- For bootstrapping
sather_h+"#include \""+comp_home+"/System/GC/gc.h\"\n";
sather_h+"#include \""+comp_home+"/System/header.h\"\n";
if prog.options.deterministic then
globals_c+"BOOL deterministic = TRUE;\n";
else
globals_c+"BOOL deterministic = FALSE;\n";
end;
sather_h+"extern jmp_buf last_protect;\n";
sather_h+"extern OB exception;\n\n";
sather_h+"extern void *sbi_alloc(size_t,INT);\n";
sather_h+"extern void *sbi_arr_alloc(size_t,INT,size_t,INT);\n";
sather_h+"extern void *sbi_alloc_atomic(size_t,INT);\n";
sather_h+"extern void *sbi_arr_alloc_atomic(size_t,INT,size_t,INT);\n";
sather_h+"extern void sbi_segfault_handler();\n\n";
sather_h+"#include \"tags.h\"\n";
sather_h+"#include \"typedefs.h\"\n";
sather_h+"#include \"decs.h\"\n";
sather_h+"#include \"globals.h\"\n";
sather_h+"#include \""+comp_home+"/System/proto.h\"\n\n";
tags:=#;
neg_tag_count:=1;
pos_tag_count:=1;
main_sig:=prog.prog_get_main.main_sig;
generate_layouts;
end;
private new_c_file is -- begin a new C file.
if ~void(code_c) then
if code_c.error then barf("Some problem writing code file");
else code_c+'\n'; code_c.close;
end;
end;
fn::=code_dir+'/'+"code"+code_c_count+".c";
code_c:=FILE::open_for_write(fn);
if code_c.error then barf("Couldn't open file "+fn); end;
code_c_count:=code_c_count+1;
code_c+"/* C code generated by Sather 1.0 compiler */\n\n";
-- should print other info here as well about compilation
code_c+"#include \"sather.h\"\n\n";
code_c+"#include \"strings.h\"\n\n";
end;
-- Because local variables have to precede any use, it is necessary to
-- queue up code until all the locals that will be needed have been
-- discovered. This is done by using the following calls.
private in is indent:=indent+1; end;
-- move indentation in a logical level
private out is indent:=indent-1; end;
-- move indentation out a logical level
private defer_newline is -- start a new line in queued-up code
routine_code:=routine_code+eol;
loop indent.times!; routine_code:=routine_code+' '; end;
end;
private newline is code_c+eol; loop indent.times!; code_c+' '; end; end;
-- start a new line to code file
private announce_at(s:SFILE_ID) is
if prog.options.debug then
-- terminate current C line and emit #line directive
lineno:INT:=s.line_num_in;
if lineno>0 then
prog.set_eloc(s);
routine_code:=
routine_code+"\n#line "+lineno+" \""+s.file_in+"\"\n";
end;
end;
end;
private eol:STR is
-- generate a newline or backslash newline, depending
-- on whether or not debugging #line directives are happening.
if prog.options.debug then return "\\\n";
-- else return "\n"; -- NLP
end; return "\n"; -- NLP
-- end; -- NLP
end;
private defer(s:STR) is routine_code:=routine_code+s; end;
-- queue up code for emmission
private ndefer(s:STR) is defer_newline; routine_code:=routine_code+s; end;
-- same as defer but emits preceding newline
comment(f:FILE,com:STR) is
-- make a C comment to a FILE
if prog.options.pretty then f+" /* "+com+" */"; end;
end;
comment(com:STR) is
-- make C comment in routine_code. Has newline.
if prog.options.pretty then ndefer("/* "+com+" */"); end;
end;
dec_local(t:$TP):STR is
-- declare local with no comment
res::=genlocal;
code_c+eol+' '+mangle(t)+' '+res+';';
return res;
end;
dec_local_comment(t:$TP,com:STR):STR is
res::=dec_local(t);
comment(code_c,com);
return res;
end;
barf(msg:STR) is barf_at(msg,void); end;
-- Something wrong within the compiler, but we can't say where.
barf_at(msg:STR,at:$PROG_ERR) is
-- Something wrong, and we know where.
prog.err_loc(at);
prog.err("Internal compiler error: "+msg);
UNIX::exit(1); -- Why bother continuing? Something's very wrong.
end;
forbid(s:STR) is forbidden:=forbidden.insert(s); end;
-- make sure this identifier never gets used
private insert_forbidden_names is
-- Insert names which must not be taken. More will also
-- be added by insert_built_in_routines.
l::=#BE_LEX(prog.options.home+"/System/FORBID");
loop forbid(l.elt!); end;
end;
private insert_built_in_routines is
-- Insert routines which are known to the compiler.
l::=#BE_LEX(prog.options.home+"/System/MACROS");
loop
ident1::=l.get_str;
ident2::=l.get_str;
ident3::=l.get_str;
if void(ident1) then break!;
elsif void(ident2) then barf("Malformed MACROS file");
elsif void(ident3) then barf("Malformed MACROS file");
else
pair:TUP{STR,STR};
if ident3="same" then pair:=#(ident2,ident2);
else pair:=#(ident2,ident3);
end;
built_in_routines:=built_in_routines.insert(ident1,pair);
-- if either starts with an identifier, forbid it
if pair.t1[0].is_alpha then
i:INT;
loop i:=1.upto!(pair.t1.length-1);
if ~pair.t1[i].is_alphanum then break!; end;
end;
forbid(pair.t1.substring(0,i-1));
end;
if pair.t2[0].is_alpha then
i:INT;
loop i:=1.upto!(pair.t2.length-1);
if ~pair.t2[i].is_alphanum then break!; end;
end;
forbid(pair.t2.substring(0,i-1));
end;
end;
end;
end;
private insert_built_in_iters is
-- Insert iters which are known to the compiler.
l::=#BE_LEX(prog.options.home+"/System/ITERS");
loop
name::=l.get_str;
s1::=l.get_str;
s2::=l.get_str;
s3::=l.get_str;
s4::=l.get_str;
if void(name) then break!;
elsif void(s1) or void(s2) or void(s3) or void(s4) then
barf("Malformed ITERS file");
else
it::=#ITER_INLINE(s1,s2,s3,s4);
built_in_iters:=built_in_iters.insert(name,it);
end;
end;
end;
private insert_special_externs is
-- Insert routines known to the compiler to need special prototypes
l::=#BE_LEX(prog.options.home+"/System/EXTERNS");
loop
ident1:STR:=l.get_str;
ident2:STR:=l.get_str;
if void(ident1) then break!;
elsif void(ident2) then barf("Malformed EXTERNS file");
else special_externs:=special_externs.insert(ident1,ident2);
end;
end;
end;
insert_built_in_classes is
-- add classes which shouldn't have layouts generated
built_in_classes:=built_in_classes.insert(prog.tp_builtin.dollar_ob);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.bool);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.char);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.int);
--built_in_classes:=built_in_classes.insert(prog.tp_builtin.inti);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.flt);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.fltd);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.fltx);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.fltdx);
--built_in_classes:=built_in_classes.insert(prog.tp_builtin.flti);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.str);
--built_in_classes:=built_in_classes.insert(prog.tp_builtin.sys);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.ext_ob);
--built_in_classes:=built_in_classes.insert(prog.tp_builtin.tp);
built_in_classes:=built_in_classes.insert(prog.tp_builtin.dollar_rehash);
--built_in_classes:=built_in_classes.insert(prog.tp_builtin.rout);
--built_in_classes:=built_in_classes.insert(prog.tp_builtin.arr_of_str);
-- make sure built-in classes mangle to the right thing.
force_mangle(prog.tp_builtin.bool,"BOOL");
force_mangle(prog.tp_builtin.char,"CHAR");
force_mangle(prog.tp_builtin.int,"INT");
force_mangle(prog.tp_builtin.flt,"FLT");
force_mangle(prog.tp_builtin.fltd,"FLTD");
force_mangle(prog.tp_builtin.fltx,"FLTX");
force_mangle(prog.tp_builtin.fltdx,"FLTDX");
force_mangle(prog.tp_builtin.str,"STR");
force_mangle(prog.tp_builtin.dollar_ob,"OB");
force_mangle(prog.tp_builtin.rout,"ROUT");
end;
is_built_in_type(t:$TP):BOOL is
return built_in_classes.test(t);
end;
private set_chks is
-- set checking states for this function
n::=current_function.tp.str;
chk_pre:=prog.options.pre_chk(n);
chk_post:=prog.options.post_chk(n);
chk_invariant:=prog.options.invariant_chk(n);
chk_assert:=prog.options.assert_chk(n);
chk_arith:=prog.options.arith_chk(n);
chk_bounds:=prog.options.bounds_chk(n);
chk_void:=prog.options.void_chk(n);
chk_when:=prog.options.when_chk(n);
chk_return:=prog.options.return_chk(n);
chk_destroy:=prog.options.destroy_chk(n);
end;
private inlined(s:SIG):BOOL is
inl::=prog.prog_am_generate.inline_tbl.get_query(s);
return ~void(inl);
end;
output_am_rout_def(func:AM_ROUT_DEF) is
if prog.show_am then #OUT+"Generating C for "+func.sig.str+'\n'; end;
if func.sig=main_sig then force_mangle(func.sig,"sather_main"); end;
if func.is_abstract then abstract_routs:=abstract_routs.push(func);
else
if inlined(func.sig) then
inlined_sigs:=inlined_sigs.insert(func.sig,func);
-- it's inline, so don't bother generating it now;
-- it will get generated if used in a dispatch table
else
start_mangling;
emit_routine(func);
end_mangling;
end;
end;
end;
private setup_routine(func:AM_ROUT_DEF) is
current_function:=func.sig;
set_chks;
current_function_str:=func.sig.str;
if func.sig.is_iter then nested_its:=#FLIST{AM_ITER_CALL_EXPR}(5);
-- that 5 is important - it keeps it from being void()
else nested_its:=void;
end;
routine_code:=#FSTR;
end;
private emit_routine(func:AM_ROUT_DEF) is
routine_count:=routine_count+1;
if routine_count>300 then
routine_count:=0;
new_c_file;
end;
setup_routine(func);
emit_prologue(func);
if is_asize(func.sig) then
if chk_void and ~prog.options.null_segfaults then
ndefer("if (self==NULL) {");
in; runtime_error("`asize' access of void",func); out;
ndefer("}");
end;
ndefer(" return self->asize;");
else emit_code(func.code);
end;
emit_epilogue(func);
emit_header(func);
-- WARNING WILL ROBINSON
-- Attempt to make the AM form become garbage by lopping
-- off any pointers to code.
func.calls:=void;
func.locals:=void;
func.code:=void;
end;
finalize is
-- finish up files, and call C compiler.
define_main_and_globals;
generate_sys_tables;
generate_dispatch_tables;
generate_bnd_rout_stubs;
generate_bnd_rout_typedefs;
if code_c.error
or typedefs_h.error
or sather_h.error
or tags_h.error
or decs_h.error
or strings_h.error
or globals_h.error
or globals_c.error
or makefile.error then
barf("Some problem writing code files.");
end;
code_c+'\n';
code_c.close;
sather_h.close;
tags_h.close;
typedefs_h.close;
decs_h.close;
strings_h+'\n';
strings_h.close;
globals_h.close;
globals_c.close;
if prog.options.verbose then
#OUT + "\nMarked read attr routs: "
+INLINE_ATTR_READ::routines;
#OUT + "\nInlined read attr calls: "
+INLINE_ATTR_READ::inlined;
#OUT + "\nMarked write attr routs: "
+INLINE_ATTR_WRITE::routines;
#OUT + "\nInlined write attr calls: "
+INLINE_ATTR_WRITE::inlined;
#OUT + "\nMarked global routs: "
+INLINE_GLOBAL_READ::routines;
#OUT + "\nInlined global calls: "
+INLINE_GLOBAL_READ::inlined;
#OUT + "\nMarked INT routs: "
+INLINE_INT_FOLD::routines;
#OUT + "\nFolded INT calls: "
+INLINE_INT_FOLD::inlined;
#OUT + "\nMarked iters: "
+built_in_iters.size;
#OUT + "\nInlined iter calls: "
+inlined_iter_count;
end;
allflags::="";
loop allflags:=allflags+' '+prog.options.c_flags.elt!; end;
if prog.options.debug then
allflags:=allflags+' '+prog.options.c_debug;
end;
if prog.options.optimize then
allflags:=allflags+' '+prog.options.c_opt;
end;
if prog.options.verbose then
allflags:=allflags+' '+prog.options.c_verbose;
end;
-- obfiles::=" globals.o"; -- NLP
obfiles::=" globals.obj"; -- NLP
loop
-- fn::=" code"+0.for!(code_c_count)+".o"; -- NLP
fn::=" code"+0.for!(code_c_count)+".obj"; -- NLP
obfiles:=obfiles+fn;
end;
-- syscom::="${CC} "; -- NLP
-- syscom:=syscom+prog.options.c_exec+"../${CS}"; -- NLP
syscom::="$(CC) "; -- NLP
syscom:=syscom+prog.options.c_exec+"../$(CS)"; -- NLP
syscom:=syscom+obfiles;
loop
s: STR := prog.options.c_files.elt!;
-- if s[0] = '/' then syscom:=syscom+' '+s; -- NLP
if s[0] = '/' or s[0] = '\\' then syscom:=syscom+' '+s; -- NLP
else syscom:=syscom+' '+"../"+s;
end; -- if
end;
loop
s: STR := prog.options.object_files.elt!;
-- if s[0] = '/' then syscom:=syscom+' '+s; -- NLP
if s[0] = '/' or s[0] = '\\' then syscom:=syscom+' '+s; -- NLP
else syscom:=syscom+' '+"../"+s;
end; -- if
end;
loop
s: STR := prog.options.archive_files.elt!;
-- if s[0] = '/' then syscom:=syscom+' '+s; -- NLP
if s[0] = '/' or s[0] = '\\' then syscom:=syscom+' '+s; -- NLP
else syscom:=syscom+' '+"../"+s;
end; -- if
end;
-- syscom:=syscom+" ${CFLAGS}"+' '+prog.options.c_compile2; -- NLP
syscom:=syscom+" $(CFLAGS)"+' '; -- NLP
loop -- NLP
c::= prog.options.c_compile2.elt!; -- NLP
if c = '/' then syscom:=syscom+'\\'; -- NLP
else syscom:=syscom+c; -- NLP
end; -- NLP
end; -- NLP
makefile+"CFLAGS= "+allflags+'\n';
makefile+"CS= "+prog.options.executable+'\n';
makefile+"CC= "+prog.options.c_compile1+'\n';
makefile+"BuildProgram: ChangeDirectory CompileAll\n\n"; -- NLP
makefile+"ChangeDirectory:\n\tcd "+code_dir+"\n"; -- NLP
-- makefile+"../${CS}:"; -- NLP
makefile+"CompileAll:"; -- NLP
-- makefile+obfiles+"\n\t"+syscom+'\n'; -- NLP
makefile+obfiles+"\n\t"+syscom+" > link.log\n"; -- NLP
makefile.close;
-- syscom:="cd "+code_dir+"; "+prog.options.make_command; -- NLP
syscom:=prog.options.make_command+" -f "+code_dir+'\\'+"Makefile"; -- NLP
if ~prog.options.verbose then
syscom:=syscom+' '+prog.options.make_silent;
end;
if prog.options.only_C then
if prog.options.verbose then #OUT + "\nSkipping make.\n"; end;
else
if prog.options.verbose then #OUT+'\n'+syscom+'\n'; end;
if UNIX::system(syscom)/=0 then barf("Make failed."); end;
end;
if ~prog.options.gen_c then
-- This is a quick hack which should be replaced!
-- dummy::=UNIX::system("rm -fr "+code_dir); -- NLP
dummy::=UNIX::system("@if exist "+code_dir+"\\*.c del "+ -- NLP
code_dir+"\\*.c"); -- NLP
dummy:= UNIX::system("@if exist "+code_dir+"\\*.obj del "+ -- NLP
code_dir+"\\*.obj"); -- NLP
dummy:= UNIX::system("@if exist "+code_dir+"\\*.h del "+ -- NLP
code_dir+"\\*.h"); -- NLP
dummy:= UNIX::system("@if exist "+code_dir+"\\Makefile del "+ -- NLP
code_dir+"\\Makefile"); -- NLP
dummy:= UNIX::system("@if exist "+code_dir+"\\link.log del "+ -- NLP
code_dir+"\\link.log"); -- NLP
dummy:=UNIX::system("@if not exist "+code_dir+"\\* rmdir "+code_dir);-- NLP
end;
end;
cast(dest_tp,src_tp:$TP,expr:STR):STR
pre dest_tp=src_tp -- make sure cast isn't nonsense
or (dest_tp.is_abstract and src_tp.is_abstract)
or dest_tp.is_subtype(src_tp)
or src_tp.is_subtype(dest_tp)
is
-- possibly convert an expression to another type to sooth C's
-- savage type beast when up- or down-typing.
res:STR;
if dest_tp.is_abstract and src_tp.is_value then -- boxing
res:=dec_local_comment(dest_tp,"local for boxed "+src_tp.str);
ndefer(res+" = ("+mangle(dest_tp)+")"+allocate(src_tp)+";");
ndefer("(("+mangle(src_tp)+"_boxed) "
+res+")->value_part = "+expr+";");
elsif dest_tp.is_value and src_tp.is_abstract then -- unboxing
res:=dec_local_comment(dest_tp,"local for unboxed "+src_tp.str);
ndefer(res+" = (("+mangle(dest_tp)+"_boxed) "
+expr+")->value_part;");
elsif dest_tp/=src_tp then res:="(("+mangle(dest_tp)+") "+expr+")";
else res:=expr;
end;
return res;
end;
sizeof(tp:$TP):STR is
-- an expression for the storage size of a given type.
if tp.is_value then return "sizeof("+mangle(tp)+")";
-- else return "sizeof(struct "+mangle(tp)+"_struct)"; -- NLP
end; return "sizeof(struct "+mangle(tp)+"_struct)"; -- NLP
-- end; -- NLP
end;
sizeof_boxed(tp:$TP):STR pre tp.is_value is
-- an expression for the size of a boxed value type
return "sizeof(struct "+mangle(tp)+"_boxed_struct)";
end;
allocate(t:$TP):STR is
-- generate call which allocates memory and fills in tag for an
-- object of type t. This properly sets the tag field too.
-- If t is a value type it allocates the boxed version.
call_string:STR;
if t.is_atomic then
call_string := "sbi_alloc_atomic(";
else
call_string := "sbi_alloc(";
end;
if t.is_value then
return "(("+mangle(t)+"_boxed) "+call_string+sizeof_boxed(t)
+", "+tag_for(t)+"))";
-- else -- NLP
end; -- NLP
return "(("+mangle(t)+") "+call_string+sizeof(t)
+", "+tag_for(t)+"))";
-- end; -- NLP
end;
array_allocate(t:$TP,n:STR):STR is
-- generate call which allocates memory and fills in tag for
-- an object of type t and an array portion with n elements.
-- This sets the tag field but NOT asize, because it isn't
-- reachable from an untyped C routine.
-- If t is a value type it allocates the boxed version.
res,call_string:STR;
t2:$TP:=prog.am_ob_def_for_tp(t).arr;
if t.is_atomic then
call_string := "sbi_arr_alloc_atomic(";
else
call_string := "sbi_arr_alloc(";
end;
if t.is_value then
res:="(("+mangle(t)+"_boxed) "+call_string+sizeof_boxed(t);
else res:="(("+mangle(t)+") "+call_string+sizeof(t);
end;
res:=res+", "+tag_for(t)+", ";
-- Use mangle(t2) for the array portion, because we want the
-- same sizeof(x) expression whether or not it is a value type
return res+"sizeof("+mangle(t2)+") , "+n+"))";
end;
default_init(t:$TP):STR is
-- string representing default initialization expression
-- for a given type.
if t.is_value then
if is_built_in_type(t) then return "(("+mangle(t)+") "+"0)";
else return mangle(t)+"_zero";
end;
-- else return "(("+mangle(t)+") "+"NULL)"; -- NLP
end; return "(("+mangle(t)+") "+"NULL)"; -- NLP
-- end; -- NLP
end;
is_const_expr(e:$AM_EXPR):BOOL is
-- is this something we can make a C initializing constant for?
if void(e) then return false; end;
typecase e
when AM_VOID_CONST then return true;
when AM_BOOL_CONST then return true;
when AM_CHAR_CONST then return true;
when AM_STR_CONST then return true;
when AM_INT_CONST then return true;
-- else return false; -- NLP
else; end; return false; -- NLP
-- end; -- NLP
end;
define_main_and_globals is
-- generate actual main call, which then calls sather_main.
-- has to initialize any globals and declare them.
main_tp::=mangle(main_sig.tp);
code_c+'\n';
comment(code_c,"Definition of main (generated)");
code_c+"\nint main(int argc, char *argv[]) {";
routine_code:=#FSTR+"\n";
in;
ndefer(main_tp+" main_ob;");
if ~void(main_sig.args) then
ndefer(mangle(prog.tp_builtin.arr_of_str)+" main_args;");
ndefer("int i,j,length;");
ndefer("STR s;");
end;
-- emit globals and any initializing expressions needed
emit_globals;
-- default object for main
ndefer("main_ob = ");
if main_sig.tp.is_value then defer(main_tp+"_zero;");
else defer(allocate(main_sig.tp)+";");
end;
-- arguments, if needed
if ~void(main_sig.args) then
ndefer("main_args = "
+array_allocate(prog.tp_builtin.arr_of_str,"argc")+";");
ndefer("main_args->asize = argc;");
ndefer("for (i=0;i<argc;i++) {");
ndefer(" for (length=0; argv[i][length]!=0; length++);");
ndefer(" s = "+array_allocate(prog.tp_builtin.str,"length")+";");
ndefer(" s->asize = length;");
ndefer(" for (j=0;j<length;j++) s->arr_part[j] = argv[i][j];");
ndefer(" main_args->arr_part[i] = s;");
ndefer("}");
end;
if prog.options.null_segfaults then
ndefer("signal(SIGSEGV,(void(*)())sbi_segfault_handler);");
end;
ndefer("if (setjmp(last_protect) == 0) {");
in;
if ~void(main_sig.ret) then
if ~void(main_sig.args) then
ndefer("return sather_main(main_ob,main_args);");
else ndefer("return sather_main(main_ob);");
end;
else
if ~void(main_sig.args) then
ndefer("sather_main(main_ob,main_args); return 0;");
else ndefer("sather_main(main_ob); return 0;");
end;
end;
out;
ndefer("} else {");
in;
ndefer("if (exception->header.tag=="+tag_for(prog.tp_builtin.str)
+") fprintf(stderr,\"Uncaught STR exception: %s\\n\","
+"((STR)exception)->arr_part);");
ndefer("else fprintf(stderr,\"Uncaught exception\\n\");");
-- ndefer("abort();"); -- NLP
ndefer("exit(16);"); -- NLP
out;
ndefer("}");
out;
code_c+routine_code+"\n}\n\n";
end;
private emit_globals is
-- emit declarations for globals and any code in main
-- that has to execute to initialize to them before other code
loop
age::=prog.global_tbl.top_sort.elt!;
if is_const_expr(age.init) then
globals_c+'\n';
if age.is_const then globals_h+"const "; end;
e:STR:=emit_expr(age.init);
if age.is_const then globals_c+"const "; end;
globals_c+mangle(age.tp)+' '+mangle(age)+" = "+e+';';
comment(globals_c,"Const "+mangle(age.class_tp)
+"::"+age.name.str);
else
if ~void(age.init) or
(age.tp.is_value and ~is_built_in_type(age.tp)) then
-- will be initialized in main
globals_c+'\n'+mangle(age.tp)+' '+mangle(age)+';';
comment(globals_c,"Shared "+mangle(age.class_tp)
+"::"+age.name.str);
comment("Initialize shared "+mangle(age.class_tp)
+"::"+age.name.str);
if ~void(age.init) then
ndefer(mangle(age)+" = "+emit_expr(age.init)+';');
else
ndefer(mangle(age)+" = "+default_init(age.tp)+';');
end;
else
globals_c+'\n'+mangle(age.tp)+' '+mangle(age)
+" = "+default_init(age.tp)+';';
comment(globals_c,"Shared "+mangle(age.class_tp)
+"::"+age.name.str);
end;
end;
comment(globals_h,"Const "+mangle(age.class_tp)
+"::"+age.name.str);
globals_h+"extern ";
globals_h+mangle(age.tp)+' '+mangle(age)+";\n";
end;
end;
generate_sys_tables is
-- make routines/tables needed by the SYS class
-- also, make const declarations for all the tags encountered
code_c+"\nSTR c_SYS_str_for_tp_INT_STR(SYS p,INT i) {\n";
code_c+" switch (i) {\n";
loop
p::=tags.pairs!;
tags_h+"#define "+mangle(p.t1)+"_tag "+p.t2+"\n";
dummy::=#AM_STR_CONST;
dummy.bval:=p.t1.str;
code_c+" case "+mangle(p.t1)+"_tag: return "
+emit_str_const(dummy)+";\n";
end;
-- code_c+" default: fprintf(stderr,\"Internal error: unknown tag?\\n\"); abort();\n"; -- NLP
code_c+" default: fprintf(stderr,\"Internal error: unknown tag?\\n\"); exit(16);\n"; -- NLP
code_c+" }\n";
code_c+"}\n\n";
code_c+"\nBOOL c_SYS_ob_eq_OB_OB_BOOL(OB o1,OB o2) {\n";
code_c+" INT t1,t2;\n";
code_c+" if (o1==o2) return TRUE;\n";
code_c+" if (o1==NULL || o2==NULL) return FALSE;\n";
code_c+" t1 = o1->header.tag; t2 = o2->header.tag;\n";
code_c+" if (t1!=t2) return FALSE;\n";
code_c+" switch (t1) {\n";
loop
tp::=tags.keys!;
tpstr::=mangle(tp);
if tp.is_value then
code_c+" case "+tpstr+"_tag:\n";
in;
-- code_c+" {"+tpstr+" v1 = (("+tpstr+"_boxed)o1)->value_part;\n"; -- NLP
-- code_c+" "+tpstr+" v2 = (("+tpstr+"_boxed)o2)->value_part;\n"; -- NLP
code_c+" {"+tpstr+" v1,v2;\n"; -- NLP
code_c+" v1 = (("+tpstr+"_boxed)o1)->value_part;\n"; -- NLP
code_c+" v2 = (("+tpstr+"_boxed)o2)->value_part;\n"; -- NLP
code_c+" return "+value_compare(tp,"v1","v2")+";}\n";
out;
end;
end;
code_c+" default: return FALSE;";
comment(code_c,"Not a value type");
code_c+"\n }\n";
code_c+"}\n\n";
end;
generate_layouts is
-- emit typedef/struct for all concrete classes
-- first, put classes on a "to do" list. This will be passed over
-- repeatedly, only emitting layouts which have value typed fields
-- which have already been emitted - C requires this @*$*&^!
todo::=#FLIST{TP_CLASS};
done::=#FSET{$TP};
loop todo:=todo.push(prog.tp_tbl.class_tbl.elt!); end;
loop until!(todo.is_empty);
next_todo::=#FLIST{TP_CLASS};
loop
tp:TP_CLASS:=todo.elt!;
if tp.kind=TP_KIND::ext_tp or is_built_in_type(tp) then
-- do nothing for external or built-in classes
done:=done.insert(tp);
elsif tp.is_abstract then
-- abstract "layouts" are really just the leading header
typedefs_h+"typedef struct "+mangle(tp)+"_struct {\n";
forbid(mangle(tp)+"_struct");
typedefs_h+" OB_HEADER header;\n";
typedefs_h+" } *"+mangle(tp)+";\n\n";
done:=done.insert(tp);
elsif tp.is_bound then barf("bound types not implemented yet");
else
-- a reference or value type
-- Is okay to make layout? if not, put on next_todo
-- list instead of dealing with it now.
okay:BOOL:=true;
l:AM_OB_DEF:=prog.am_ob_def_for_tp(tp);
if ~void(l.at) then
loop ci::=l.at.targets!;
-- changed following line from ...class(tp)
if ci.is_value and ~is_built_in_type(ci)
and ~done.test(ci) then okay:=false;
--#OUT + tp.str+" needs "+ci.str+'\n';
end;
end;
end;
-- added is_built_in test on following line
if ~void(l.arr) and l.arr.is_value
and ~is_built_in_type(l.arr)
and ~done.test(l.arr) then okay:=false;
-- #OUT + tp.str+" needs "+l.arr.str+'\n';
end;
if ~okay then next_todo:=next_todo.push(tp);
else
done:=done.insert(tp);
cname:STR:=mangle(l.tp);
typedefs_h+"typedef struct "+cname+"_struct {";
comment(typedefs_h,"layout for "+l.tp.str);
typedefs_h+'\n';
forbid(mangle(tp)+"_struct");
if ~tp.is_value then
typedefs_h+" OB_HEADER header;\n";
end;
if ~void(l.at) then
loop p::=l.at.pairs!;
if is_built_in_type(p.t2) then
typedefs_h+' '+mangle(p.t2)
+' '+mangle(p.t1)+";\n";
elsif ~p.t2.is_value then
typedefs_h+" struct "+mangle(p.t2)
+"_struct *"+mangle(p.t1)+";\n";
else -- user-defined value class
typedefs_h+" struct "+mangle(p.t2)
+"_struct "+mangle(p.t1)+";\n";
end;
end;
end;
if ~void(l.arr) then
if ~tp.is_value then typedefs_h+" INT asize;\n"; end;
if is_built_in_type(l.arr) then
typedefs_h+' '+mangle(l.arr)+" arr_part[";
elsif ~l.arr.is_value then
typedefs_h+" struct "+mangle(l.arr)
+"_struct *arr_part[";
else -- user-defined value class
typedefs_h+" struct "+mangle(l.arr)
+"_struct arr_part[";
end;
typedefs_h+1.max(l.asize)+"];\n";
end;
if tp.is_value then
typedefs_h+" } "+cname+';'+'\n';
typedefs_h+"static "+cname+" "+cname+"_blob;\n"; -- NLP
typedefs_h+"static "+cname+" "+cname+"_zero;";
comment(typedefs_h,"automatically initialized");
typedefs_h+'\n'+'\n';
forbid(cname+"_blob"); -- NLP
forbid(cname+"_zero");
typedefs_h+"typedef struct "+cname+"_boxed_struct {\n";
typedefs_h+" OB_HEADER header;\n";
typedefs_h+' '+cname+" value_part;\n";
typedefs_h+" } *"+cname+"_boxed;\n\n";
forbid(cname+"_boxed");
forbid(cname+"_boxed_struct");
else
typedefs_h+" } *"+cname+";\n\n";
end;
end;
end;
end;
assert next_todo.size<todo.size;
todo:=next_todo;
end; -- loop
end;
private generate_bnd_rout_typedefs is
-- generate defs for bound routine objects.
loop
e::=prog.tp_tbl.rout_tbl.elt!;
name::=mangle(e);
forbid(name+"_struct");
if ~is_built_in_type(e) then
typedefs_h+"typedef struct "+name+"_struct {\n";
typedefs_h+" OB_HEADER header;\n";
if ~void(e.ret) then typedefs_h+' '+mangle(e.ret);
else typedefs_h+" void";
end;
typedefs_h+" (*funcptr)(void *";
loop
a::=e.args.elt!;
typedefs_h+", "+mangle(a);
end;
typedefs_h+");\n} *"+name+";\n\n";
end;
end;
end;
emit_typedef_for_iter(f:AM_ROUT_DEF) is
typedefs_h+"\ntypedef struct "+mangle(f.sig)+"_frame_struct {\n";
forbid(mangle(f.sig)+"_frame_struct");
-- make slot for each argument
loop
fi::=f.elt!;
typedefs_h+' '+mangle(fi.tp)+' '+"arg"+0.up!+';';
comment(typedefs_h,"Formal argument: "+fi.name.str);
typedefs_h+'\n';
end;
-- make slot for each local
if ~void(f.locals) then
loop
fli::=f.locals.elt!;
name::=mangle(fli);
name:=name.tail(name.length-7);
typedefs_h+' '+mangle(fli.tp)+' '+name+';';
comment(typedefs_h,"local");
typedefs_h+'\n';
end;
end;
-- slot for any nested iter frames
loop
ni::=nested_its.elt!;
typedefs_h+" struct "+mangle(ni.fun)+"_frame_struct *";
name:STR:=mangle(ni);
name:=name.tail(name.length-7);
typedefs_h+name+"; /* nested iter frame */\n";
end;
-- finally, a slot for the state number
typedefs_h+" INT state;\n";
typedefs_h+" } *"+mangle(f.sig)+"_frame;\n\n";
forbid(mangle(f.sig)+"_frame");
end;
emit_header(f:AM_ROUT_DEF) is
-- emit ANSI header, and also struct to hold locals if an iter
sig:SIG:=f.sig;
-- if an iter, do typedef with same name for holding the frame
if f.is_iter then emit_typedef_for_iter(f); end;
if ~void(sig.ret) then decs_h+mangle(sig.ret)+' ';
else decs_h+"void ";
end;
decs_h+mangle(f.sig)+'(';
-- if an iter, just a pointer for frame struct
-- otherwise, pass arguments the usual way
if f.is_iter then decs_h+mangle(f.sig)+"_frame";
elsif f.is_abstract then
decs_h+mangle(f.sig.tp);
if ~void(f.sig.args) then
loop decs_h+", "+mangle(f.sig.args.elt!); end;
end;
elsif f.is_external then
-- an external routine with a body still doesn't have a self
flag::=false;
loop
s::=mangle(f.elt!.tp);
if flag then decs_h+", ".separate!(s); end;
flag:=true;
end;
else
loop decs_h+", ".separate!(mangle(f.elt!.tp)); end;
end;
decs_h+");\n";
end;
emit_prologue(f:AM_ROUT_DEF) pre ~void(f.sig) is
saw_outer_return:=false;
sig:SIG:=f.sig;
newline; newline;
comment(code_c,"Definition of "+sig.str);
code_c+'\n';
if ~void(sig.ret) then code_c+mangle(sig.ret)+' ';
else code_c+"void ";
end;
if f.is_external then
force_mangle(sig,f.sig.tp.str+'_'+f.sig.name.str);
end;
code_c+mangle(sig)+'(';
-- if an iter, pointer for frame, otherwise regular args
if f.is_iter then
-- just a single frame argument
code_c+mangle(f.sig)+"_frame frame";
-- other locals now prepend "frame->"
if ~void(f.locals) then
loop
lv:AM_LOCAL_EXPR:=f.locals.elt!;
was:STR:=mangle(lv);
remangle(lv,"frame->"+was);
end;
end;
-- also, arguments are on frame
loop
lv:AM_LOCAL_EXPR:=f.elt!;
remangle(lv,"frame->arg"+0.up!);
end;
else
-- if not an iter, declare arguments
if f.is_abstract then
-- in abstract routine, arg names are canonical
code_c+mangle(f.sig.tp)+" arg0";
if ~void(f.sig.args) then
loop
e::=f.sig.args.elt!;
code_c+", "+mangle(e)+" arg"+1.up!;
end;
end;
elsif f.is_external then
flag::=false;
loop
e::=f.elt!;
assert ~void(e) and ~void(e.tp);
if flag then
code_c+", ".separate!(mangle(e.tp)+' '+mangle(e));
end;
flag:=true;
end;
else
loop
e::=f.elt!;
assert ~void(e) and ~void(e.tp);
code_c+", ".separate!(mangle(e.tp)+' '+mangle(e));
end;
end;
end;
current_self:=mangle(f[0]);
code_c+") {";
in;
-- now emit local declarations (if an iter, they are on the
-- frame and don't need to be declared).
if ~f.is_iter and ~void(f.locals) then
loop
lv:AM_LOCAL_EXPR:=f.locals.elt!;
assert ~void(lv) and ~void(lv.tp);
newline;
if lv.is_volatile then code_c+"volatile "; end;
if lv.needs_init or lv.tp.is_value then
def:STR:=default_init(lv.tp);
code_c+mangle(lv.tp)+' '+mangle(lv)+" = "+def+';';
else
code_c+mangle(lv.tp)+' '+mangle(lv)+';';
end;
end;
end;
-- if an iter, maybe return a dummy value when quit, so declare
-- one. Also generate switch statement. The first state
-- initializes any locals to the iter that need it. (It shouldn't
-- be possible to get there more than once in an invocation.)
if f.is_iter then
if ~void(sig.ret) then
newline;
code_c+mangle(sig.ret)+' '+"dummy;";
end;
ndefer("switch (frame->state) {");
in;
loop
i::=0.upto!(f.num_yields);
ndefer("case "+i+": goto state"+i+';');
end;
ndefer("}");
out;
ndefer("state0:;");
state_counter:=1;
-- initialize any locals that need it
if ~void(f.locals) then
loop i::=f.locals.elt!;
if i.needs_init then
ndefer(mangle(i)+" = "+default_init(i.tp)+";");
end;
end;
end;
end;
end;
emit_epilogue(f:AM_ROUT_DEF) is
if chk_return and ~saw_outer_return
and ~f.is_iter and
~void(f.sig.ret) then
-- if it has a return value, it is necessary to
-- make sure doesn't exit without a return
runtime_error("Last statement wasn't return",f);
end;
code_c+routine_code; -- output all the code
if f.is_iter then -- add an explicit 'quit'
newline; code_c+"frame->state = -1;";
if ~void(f.sig.ret) then newline; code_c+"return dummy;";
else newline; code_c+"return;";
end;
end;
out; newline; code_c+"}";
-- restore names of local variables and arguments
if ~void(f.locals) and ~f.is_iter then
loop unmangle(f.locals.elt!); end;
end;
loop unmangle(f.elt!); end;
end;
private generate_bnd_rout_stubs is
-- Generate typedefs for bound routine objects and
-- make stub functions to execute them
loop
e::=bnd_rout_creates.elt!;
name::=mangle(e);
forbid(name+"_ob");
forbid(name+"_ob_struct");
typedefs_h+"typedef struct "+name+"_ob_struct {\n";
typedefs_h+" OB_HEADER header;\n";
if ~void(e.fun.ret) then code_c+mangle(e.fun.ret)+' ';
else code_c+"void ";
end;
code_c+name+'('+name+"_ob ob";
if ~void(e.fun.ret) then decs_h+mangle(e.fun.ret)+' ';
else decs_h+"void ";
end;
decs_h+name+'('+name+"_ob";
if ~void(e.fun.ret) then typedefs_h+' '+mangle(e.fun.ret)+' ';
else typedefs_h+" void ";
end;
typedefs_h+"(*funcptr)(struct "+name+"_ob_struct *";
loop
i::=e.unbnd_args.elt!;
dec:STR;
if i=0 then dec:=mangle(e.fun.tp);
else dec:=mangle(e.fun.args[i-1]);
end;
code_c+", "+dec+" unbound_arg"+0.up!;
decs_h+", "+dec;
typedefs_h+", "+dec;
end;
decs_h+");\n";
code_c+") {\n";
typedefs_h+");\n";
loop
i::=e.ind!;
if e.bnd_args[i]=0 then
typedefs_h+' '+mangle(e.fun.tp);
else
typedefs_h+' '+mangle(e.fun.args[i-1]);
end;
typedefs_h+" bound_arg"+i+";\n";
end;
typedefs_h+" } *"+name+"_ob;\n\n";
arg_list::=#ARRAY{STR}(e.fun.args.size+1);
-- Make a dummy routine call and generate it
bnd::=0; -- The index of the next bound argument
unbnd::=0; -- The index of the next unbound argument
is_bnd:BOOL; -- So, is the next arg bound or unbound?
loop
i::=arg_list.ind!; -- The index we're on.
if bnd<e.bnd_args.size then
if e.bnd_args[bnd]=i then is_bnd:=true;
elsif e.unbnd_args[unbnd]=i then is_bnd:=false;
else barf("Ran off unbound arg list");
end;
elsif e.unbnd_args[unbnd]=i then is_bnd:=false;
else barf("Ran off unbound arg list 2nd");
end;
if is_bnd then
arg_list[i]:="ob->bound_arg"+bnd;
bnd:=bnd+1;
else
arg_list[i]:="unbound_arg"+unbnd;
unbnd:=unbnd+1;
end;
end;
if ~void(e.fun.ret) then code_c+" return"; end;
code_c+' '+emit_call(e.fun,arg_list)+";\n}\n\n";
end;
end;
private generate_dispatch_tables is
loop emit_dispatch_table(abstract_routs.elt!); end;
end;
private emit_dispatch_wrapper(s:SIG):STR is
-- emit a wrapper function for unboxing value types
-- when dispatched. Return the function name generated.
-- until mangling is really correct, use func_unbox as name
res::=mangle(s)+"_unbox";
code_c+'\n';
comment(code_c,"Wrapper to unbox "+s.str);
code_c+'\n';
if void(s.ret) then code_c+"void "; decs_h+"void ";
else code_c+mangle(s.ret)+' '; decs_h+mangle(s.ret)+' ';
end;
code_c+res+"("+mangle(s.tp)+"_boxed arg0";
decs_h+res+"("+mangle(s.tp)+"_boxed";
loop
e::=s.args.elt!;
code_c+", "+mangle(e)+" arg"+1.up!;
decs_h+", "+mangle(e);
end;
code_c+") {\n";
decs_h+");\n";
code_c+' ';
if ~void(s.ret) then code_c+"return "; end;
code_c+mangle(s)+"(arg0->value_part";
loop i::=1.upto!(s.num_args);
code_c+", arg"+i;
end;
code_c+");\n"+"}\n";
return res;
end;
private emit_dispatch_table(f:AM_ROUT_DEF) is
-- emit function pointer table for dispatched routines and iters.
-- first, collect descendents' info. We want to make the smallest
-- table possible, so find the min and max tags needed.
des::=#FLIST{$TP};
mintag::=INT::maxint;
maxtag::=INT::minint;
cst:STR; -- Cast to correct function pointer type
decl:STR; -- NLP
gh:FSET{$TP};
fst::=f.sig.tp;
typecase fst
when TP_CLASS then gh:=prog.descendants_of_abs(fst);
end;
loop t::=gh.elt!;
des:=des.push(t);
tag:INT:=num_tag_for(t);
maxtag:=maxtag.max(tag);
mintag:=mintag.min(tag);
end;
comment(globals_c,"Dispatch table for "+mangle(f.sig));
globals_h+"\nextern const int "+mangle(f.sig)+"_offset;\n";
globals_c+"\nconst int "+mangle(f.sig)+"_offset = "+(-mintag)+";\n";
forbid(mangle(f.sig)+"_offset");
-- decl::="const "; -- NLP
-- cst:="(const "; -- NLP
cst:="(";
if ~void(f.sig.ret) then
-- decl:=decl+mangle(f.sig.ret); -- NLP
decl:=mangle(f.sig.ret); -- NLP
cst:=cst+mangle(f.sig.ret);
else -- NLP
decl:="int"; -- NLP
cst:=cst+"int"; -- NLP
end;
-- decl:=decl+" (*"+mangle(f.sig)+"[])("+mangle(f.sig.tp); -- NLP
decl:=decl+" (* const "+mangle(f.sig)+"[])("+mangle(f.sig.tp); -- NLP
cst:=cst+" (*)("+mangle(f.sig.tp);
if ~void(f.sig.args) then
loop
e::=f.sig.args.elt!;
decl:=decl+", "+mangle(e);
cst:=cst+", "+mangle(e);
end;
end;
decl:=decl+")";
globals_h+"extern "+decl+";\n";
globals_c+decl+" = {\n";
cst:=cst+"))";
-- Manufacture table initialization
-- this is quadratic in number of descendents
loop
i::=mintag.upto!(maxtag);
exists:BOOL:=false;
loop
e::=des.elt!;
tag:INT:=num_tag_for(e);
real_sig:SIG:=prog.ifc_tbl.ifc_of(e).sig_conforming_to(f.sig);
if tag=i then
exists:=true;
functocall:STR;
-- if we encounter an inlined routine, then
-- it wasn't generated so we need to do it now.
am::=inlined_sigs.get(real_sig);
if ~void(am) then
emit_routine(am);
inlined_sigs:=inlined_sigs.delete(real_sig);
end;
if e.is_value then
functocall:=emit_dispatch_wrapper(real_sig);
else
functocall:=mangle(real_sig);
end;
globals_c+' '+cst+functocall;
if i/=maxtag then globals_c+","; end;
comment(globals_c,real_sig.str);
globals_c+'\n';
end;
end;
if ~exists then
globals_c+" NULL";
if i/=maxtag then globals_c+","; end;
globals_c+'\n';
end;
end;
if mintag>maxtag then
globals_c+" NULL /* No descendents found - how odd. */\n";
end;
globals_c+"};\n";
end;
emit_code(arg:$AM_STMT) is
-- emit code associated with sequence of $AM_STMTs
s1,s2:STR;
loop until!(void(arg));
announce_at(arg.source);
typecase arg
when AM_ASSIGN_STMT then
s1:=emit_expr(arg.dest);
s2:=emit_expr(arg.src);
ndefer(s1+" = "+cast(arg.dest.tp,arg.src.tp,s2)+';');
when AM_IF_STMT then
s1:=emit_expr(arg.test);
ndefer("if ("+s1+") {"); in;
emit_code(arg.if_true);
out; ndefer("}");
if ~void(arg.if_false) then
ndefer("else {");
in;
emit_code(arg.if_false);
out; ndefer("}");
end;
when AM_LOOP_STMT then
outer_loop:STR:=current_loop;
current_loop:=mangle(arg);
fname:STR:=genother; fnamecount::=0;
if ~void(arg.bits) or ~void(arg.its) then
comment("loop");
ndefer("{"); in;
end;
if ~void(arg.bits) then
defer_newline;
barf_at("Bound iters not implemented",arg);
code_c+"<<initialize bound iters here>>";
end;
if ~void(arg.its) then
loop e::=arg.its.elt!;
if current_function.is_iter or ~is_built_in_iter(e.fun) then
comment("Frame for call to "+e.fun.str);
if ~void(nested_its) then
-- inside an iter, so nested frames must be
-- placed in this frame instead of as locals.
-- make sure same nested iter found only once
assert ~nested_its.contains(e);
nested_its:=nested_its.push(e);
force_mangle(e,"frame->nested"
+nested_its.size);
end;
defer_newline;
if arg.has_yield then
if void(nested_its) then
defer(mangle(e.fun)+"_frame ");
end;
defer(mangle(e)+" = ALLOCATE("
+mangle(e.fun)+"_frame);");
else
tname::=fname+'_'+fnamecount;
fnamecount:=fnamecount+1;
defer("struct "+mangle(e.fun)
+"_frame_struct "+tname+';');
defer_newline;
if void(nested_its) then
defer(mangle(e.fun)+"_frame ");
ndefer(mangle(e)+" = &"+tname+';');
end;
end;
else
ndefer("BOOL "+mangle(e)+" = TRUE;");
end;
end;
-- make pointers to frames which are really on the stack
-- this couldn't be done above because in C all decs
-- must proceed ordinary assignments.
if ~void(nested_its) then
fnamecount:=0;
loop e::=arg.its.elt!;
if current_function.is_iter or ~is_built_in_iter(e.fun) then
if ~arg.has_yield then
ndefer(mangle(e)+" = &"+fname
+'_'+fnamecount+';');
fnamecount:=fnamecount+1;
end;
end;
end;
end;
-- initialize all iter states
loop
it::=arg.its.elt!;
if current_function.is_iter or ~is_built_in_iter(it.fun) then
ndefer(mangle(it)+"->state = 0;");
else
end;
end;
end;
ndefer("while (1) {");
in;
emit_code(arg.body);
out;
ndefer("}");
if ~void(arg.bits) or ~void(arg.its) then
out; ndefer("}");
end;
ndefer(current_loop+": ;");
-- Explicitly free any heap-allocated frames
if arg.has_yield and ~void(arg.its) then
loop e::=arg.its.elt!;
if current_function.is_iter or ~is_built_in_iter(e.fun) then
ep::=mangle(e);
ndefer("GC_free("+ep+"); "+ep+" = NULL;");
end;
end;
end;
current_loop:=outer_loop;
when AM_BREAK_STMT then
ndefer("goto "+current_loop+";");
when AM_RETURN_STMT then
if current_function.is_iter then
ndefer("frame->state = -1;");
end;
if ~void(arg.val) then
s1:=emit_expr(arg.val);
ndefer("return "
+cast(current_function.ret,arg.val.tp,s1)+';');
else
assert void(current_function.ret)
or current_function.is_iter;
ndefer("return;");
end;
if indent=1 then saw_outer_return:=true; end;
when AM_EXPR_STMT then
s1:=emit_expr(arg.expr);
if ~void(s1) then
if ~void(arg.expr.tp) then ndefer("(void) "+s1+';');
else ndefer(s1+';');
end;
end;
when AM_YIELD_STMT then
ndefer("frame->state = "+arg.ret+';');
if ~void(arg.val) then
ndefer("return "
+cast(current_function.ret,arg.val.tp,
emit_expr(arg.val))+';');
else
ndefer("return;");
end;
ndefer("state"+state_counter+":;");
state_counter:=state_counter+1;
when AM_CASE_STMT then
targets:ARRAY{ARRAY{STR}};
test:STR:=emit_expr(arg.test);
-- produce C expressions for all target expressions
if ~void(arg.tgts) then
targets:=#ARRAY{ARRAY{STR}}(arg.tgts.size);
loop
i::=targets.ind!;
targets[i]:=#ARRAY{STR}(arg.tgts[i].size);
loop
j::=targets[i].ind!;
targets[i][j]:=emit_expr(arg.tgts[i][j]);
end;
end;
ndefer("switch ("+test+") {");
in;
comment("case statement");
loop
i::=targets.ind!;
loop ndefer("case "+targets[i].elt!+':'); end;
in; emit_code(arg.stmts[i]); out;
ndefer(" break;");
end;
ndefer("default: ;");
in;
if arg.no_else then
runtime_error(
"No applicable target in case statement",arg);
else emit_code(arg.else_stmts);
end;
out; out;
ndefer("}");
else
runtime_error(
"No applicable target in case statement",arg);
end;
when AM_PRE_STMT then
if chk_pre then
ndefer("if (!("+emit_expr(arg.test)+")) {");
in; runtime_error("Violation of precondition",arg);out;
ndefer("}");
end;
when AM_POST_STMT then
if chk_post then
ndefer("if (!("+emit_expr(arg.test)+")) {");
in;
runtime_error("Violation of postcondition",arg);
out;
ndefer("}");
end;
when AM_INITIAL_STMT then
if chk_post then emit_code(arg.stmts); end;
when AM_ASSERT_STMT then
if chk_assert then
ndefer("if (!("+emit_expr(arg.test)+")) {");
in; runtime_error("Violation of assertion",arg); out;
ndefer("}");
end;
when AM_TYPECASE_STMT then
if arg.has_void_stmts or chk_when then
ndefer("if ("+mangle(arg.test)+"==NULL) {");
in;
if arg.has_void_stmts then emit_code(arg.void_stmts);
else runtime_error("Void object of typecase",arg);
end;
out;
ndefer("} else"); -- DPS seemed wrong, added else
end;
emit_typeswitch(mangle(arg.test),arg.tgts,arg.stmts);
if arg.no_else then
in;
runtime_error("No applicable type in typecase",arg);
out;
else
in; emit_code(arg.else_stmts); out;
end;
out;
ndefer("}");
when AM_RAISE_STMT then
assert ~void(arg.val);
ndefer("exception = "+cast(prog.tp_builtin.dollar_ob,
arg.val.tp,emit_expr(arg.val))+";");
ndefer("longjmp(last_protect,1);");
when AM_INVARIANT_STMT then
if chk_invariant then
assert ~void(current_self);
ndefer("if (!"+mangle(arg.sig)
+"("+current_self+")) {");
in;
runtime_error("Failed invariant "+arg.sig.str,arg);
out;
ndefer("}");
end;
-- from here on haven't been implemented yet.
when AM_PROTECT_STMT then
ndefer("{");
in;
ndefer("OB old_exception = exception;");
ndefer("jmp_buf old_protect;");
ndefer("bcopy(last_protect,old_protect,sizeof(jmp_buf));");
ndefer("if (setjmp(last_protect) == 0) {");
in;
emit_code(arg.body);
out;
ndefer("} else {");
in;
ndefer("bcopy(old_protect,last_protect,sizeof(jmp_buf));");
emit_typeswitch("exception",arg.tgts,arg.stmts);
if arg.no_else then
ndefer("longjmp(last_protect,1);");
else
in; emit_code(arg.else_stmts); out;
end;
out;
ndefer("}"); -- closes type switch
out;
ndefer("}"); -- closes if
ndefer("bcopy(old_protect,last_protect,sizeof(jmp_buf));");
ndefer("exception = old_exception;");
out;
ndefer("}"); -- closes local scope
end;
arg:=arg.next;
end;
end;
private emit_typeswitch(test:STR,tgts:FLIST{$TP},stmts:FLIST{$AM_STMT}) is
-- Emit a structure that switches on type. This is used
-- by both protect and typecase statements. It stops after
-- emitting the "default:" entry which should then be generated
-- appropriately by the caller, along with an "out;" and
-- closing curly braces.
ndefer("switch ("+test+"->header.tag) {");
in;
if ~void(tgts) then
gen::=#FSET{$TP};
loop
i::=tgts.ind!;
tp::=tgts[i];
dseen:BOOL:=false;
if tp.is_abstract then
typecase tp
when TP_CLASS then
loop
d::=prog.descendants_of_abs(tp).elt!;
if ~gen.test(d) then
ndefer("case "+tag_for(
prog.descendants_of_abs(tp).elt!)+':');
dseen:=true;
gen:=gen.insert(d);
end;
end;
end;
else
if ~gen.test(tp) then
ndefer("case "+tag_for(tp)+':');
dseen:=true;
gen:=gen.insert(tp);
end;
end;
-- only emit code if some descendent was seen
if dseen then
in;
emit_code(stmts[i]);
defer(" break;");
out;
end;
end;
end;
ndefer("default: ;");
end;
private runtime_error(s:STR) is
-- emit (deferred) code to generate a fatal error at runtime.
ndefer("fprintf(stderr,\""+s+"\\n\");");
-- ndefer("abort();"); -- NLP
ndefer("exit(16);"); -- NLP
end;
private runtime_error(s:STR,l:$PROG_ERR) is
-- emit (deferred) code to generate a fatal error at runtime.
ndefer("fprintf(stderr,\""+s+" "+l.source.str+"\\n\");");
-- ndefer("abort();"); -- NLP
ndefer("exit(16);"); -- NLP
end;
private emit_args(arg:$AM_CALL_EXPR):ARRAY{STR} is
-- declare auto variables for any subexpressions that
-- can't be in-line; at the moment that means anything
-- which is a call.
res::=#ARRAY{STR}(arg.asize);
is_ext::=false;
typecase arg when AM_EXT_CALL_EXPR then is_ext:=true; else end;
-- first, find the last argument which isn't a call
last:INT;
loop last:=(arg.asize-1).downto!(-1);
while!(last>=0);
e::=arg[last];
typecase e when $AM_CALL_EXPR then break!; else end;
end;
last:=(last+1).min(arg.asize-1);
loop i::=0.for!(arg.asize);
if ~(is_ext and i=0) then
e::=arg[i];
res[i]:=emit_expr(e);
typecase e
when AM_LOCAL_EXPR then -- locals can't be affected
else
if i<last then
subexpr::=res[i];
res[i]:=dec_local(e.tp);
ndefer(res[i]+" = "+subexpr+';');
end;
end;
end;
end;
return res;
end;
private is_asize(s:SIG):BOOL is
-- True if this call should be the built-in "asize".
return s.name.str="asize"
and ~s.tp.is_abstract
and ~void(prog.impl_tbl.impl_of(s.tp).arr)
and s.num_args=0
and ~void(s.ret)
and ~s.tp.is_value;
end;
is_built_in_routine(s:SIG):BOOL is
return built_in_routines.test(s.str);
end;
is_built_in_iter(s:SIG):BOOL is
return built_in_iters.test(s.str);
end;
built_in_which_may_be_emitted_anyway(s:SIG):BOOL is
-- added by Matt Kennel, UCSD.
-- if chk_bounds or chk_arith is on, and if
-- the third column is "nomacro", then full unmacroized
-- Sather code may be emitted for some built in routines
-- never the less. This function ought to return true
-- for those built in routines which may need to be written
-- out in programmed form anyway.
-- find out if any bounds check may be done?
if ~void(prog.options.bounds_in) or prog.options.bounds_all or
~void(prog.options.arith_in) or prog.options.arith_all
-- can only worry about it if there are some bounds checks
then
tuple ::= built_in_routines.get(s.str);
if void(tuple) then return false;
else
if tuple.t2 = "nomacro" then return true;
else return false; end;
end;
-- else return false; -- NLP
end; return false; -- NLP
-- end; -- NLP
end;
private emit_rout_call(arce:AM_ROUT_CALL_EXPR):STR is
arg_list::=emit_and_cast_args(arce);
return emit_call(arce.fun,arg_list);
end;
private emit_and_cast_args(arce:AM_ROUT_CALL_EXPR):ARRAY{STR} is
-- emit args, properly casted for the given routine call
arg_list::=emit_args(arce);
arg_list[0]:=cast(arce.fun.tp,arce[0].tp,arg_list[0]);
loop
i::=1.upto!(arg_list.size-1);
arg_list[i]:=cast(arce.fun.args[i-1],arce[i].tp,arg_list[i]);
end;
return arg_list;
end;
private emit_call(fun:SIG, arg_list:ARRAY{STR}):STR is
-- assumes all args are appropriately casted already
res:STR;
-- find out if this requires special handling
biname:STR:=void;
process_as_builtin:BOOL:= fun.is_builtin;
if process_as_builtin and (chk_arith or chk_bounds) then
biname:=built_in_routines.get(fun.str).t2;
if biname = "nomacro" then process_as_builtin := false; end;
end;
if process_as_builtin then
if chk_arith then biname:=built_in_routines.get(fun.str).t2;
else biname:=built_in_routines.get(fun.str).t1;
end;
if biname[0]='@' then
case biname[1]
when '1' then -- this happens for SYS::ob_eq($OB,$OB):BOOL
-- If both arguments are already pointers, just compare
-- them; otherwise, we have to do function call
carg1::=arg_list[1];
carg2::=arg_list[2];
if fun.args[0].is_value or fun.args[1].is_value
or fun.args[0].is_abstract
or fun.args[1].is_abstract then
return "c_SYS_ob_eq_OB_OB_BOOL("
+carg1+','+carg2+')';
else
return "("+carg1+"=="+carg2+')';
end;
else barf("Don't recognize @ function in MACROS");
end;
end;
res:="(";
i:INT:=0;
loop
until!(i>=biname.length);
if biname[i]='#' then
i:=i+1;
case biname[i]
when '1' then res:=res+arg_list[0];
when '2' then res:=res+arg_list[1];
when '3' then res:=res+arg_list[2];
when '4' then res:=res+arg_list[3];
when '5' then res:=res+arg_list[4];
else barf("Bad # spec in MACROS");
end;
else
res:=res+biname[i];
end;
i:=i+1;
end;
return res+')';
else
-- not in table, but maybe 'asize'
if is_asize(fun) then
if chk_void and ~prog.options.null_segfaults then
ndefer("if ("+arg_list[0]+"==NULL) {");
in; runtime_error("`asize' access of void in "+current_function_str); out;
ndefer("}");
end;
return "("+arg_list[0]+"->asize)";
elsif fun.tp.is_abstract then
-- put self in a local so it isn't called twice
self_ob::=dec_local(fun.tp);
ndefer(self_ob+" = "+arg_list[0]+';');
arg_list[0]:=self_ob;
if chk_void and ~prog.options.null_segfaults then
ndefer("if ("+arg_list[0]+"==NULL) {");
in; runtime_error("Dispatch on void in "+current_function_str); out;
ndefer("}");
end;
res:="(*"+mangle(fun)+"["
+arg_list[0]+"->header.tag+"
+mangle(fun)+"_offset])(";
else
-- must be ordinary call
res:=mangle(fun)+'(';
end;
end;
-- emit the argument identifiers.
res:=res+arg_list[0];
loop i::=1.upto!(arg_list.size-1);
res:=res+", "+arg_list[i];
end;
return res+')';
end;
private emit_builtin_iter_call(aice:AM_ITER_CALL_EXPR):STR is
inlined_iter_count:=inlined_iter_count+1;
it::=built_in_iters.get(aice.fun.str);
if void(it) then barf("Couldn't get iter inlining info"); end;
not_seen::=mangle(aice);
this:STR;
if ~void(aice.fun.ret) then
this:=dec_local_comment(aice.fun.ret,"Inlined return value");
else
this:=not_seen+'t'; forbid(this);
end;
-- First argument is never hot
arg1::=dec_local_comment(aice.fun.tp,"Inlined self to iter");
-- Second argument may not exist and may be hot
arg2nothot::=void(aice.fun.hot) or ~aice.fun.hot[0];
arg2:STR;
if aice.size>1 then
if arg2nothot then
arg2:=dec_local_comment(aice.fun.args[0],
"Inlined 1st arg to iter");
else
arg2:=emit_expr(aice[1]);
end;
end;
code_c+eol+' '+iter_inline_xlate(this,arg1,arg2,it.at_decs);
ndefer("if ("+not_seen+") {");
in;
ndefer(not_seen+" = FALSE;");
comment("Initialize inlined once arguments of call to "+aice.fun.str);
emit_code(aice.init);
ndefer(arg1+" = "+emit_expr(aice[0])+';');
if ~void(arg2) and arg2nothot then
ndefer(arg2+" = "+cast(aice.fun.args[0],aice[1].tp,
emit_expr(aice[1]))+';');
end;
ndefer(iter_inline_xlate(this,arg1,arg2,it.when_first_seen));
out;
ndefer("} else {");
in; ndefer(iter_inline_xlate(this,arg1,arg2,it.after)); out;
ndefer("}");
ndefer(iter_inline_xlate(this,arg1,arg2,it.before));
if ~void(aice.fun.ret) then return this;
-- else return "0 /* no return value from inlined iter */"; -- NLP
end; return "0 /* no return value from inlined iter */"; -- NLP
-- end; -- NLP
end;
private iter_inline_xlate(this,arg1,arg2,s:STR):STR is
-- give special meaning to strings used for inlining iters
res::="";
i::=0;
loop
until!(i>=s.size);
c::=s[i];
case c
when '@' then res:=res+"goto "+current_loop;
when '#' then
i:=i+1;
case s[i]
when '#' then res:=res+this;
when '1' then res:=res+arg1;
when '2' then res:=res+arg2;
else barf("Couldn't interpret # in iter inline");
end;
else res:=res+c;
end;
i:=i+1;
end;
return res;
end;
emit_iter_call(aice:AM_ITER_CALL_EXPR):STR is
s1,res:STR;
if ~current_function.is_iter and is_built_in_iter(aice.fun) then
return emit_builtin_iter_call(aice);
end;
if aice.fun.tp.is_abstract then
barf_at("Dispatched iters not implemented.",aice);
end;
if ~void(aice.tp) then
-- local variable to hold result (since we have to imbed
-- in a control structure to check for possible termination)
s1:=dec_local_comment(aice.tp,
"Holds result of call to "+aice.fun.str);
end;
-- if first time through, compute once arguments
-- (all once args before any hot args).
ndefer("if ("+mangle(aice)+"->state == 0) {");
in;
comment("Initialize once arguments of call to "+aice.fun.str);
emit_code(aice.init);
-- for each once argument, copy into frame
loop
i::=aice.ind!;
-- beware the difference in argument indices between
-- aice[] and aice.fun.hot[]!!!
if i=0 then
ndefer(mangle(aice)+"->arg"+i+" = "+emit_expr(aice[i])+';');
elsif void(aice.fun.hot) or ~aice.fun.hot[i-1] then
ndefer(mangle(aice)+"->arg"+i+" = "
+cast(aice.fun.args[i-1],aice[i].tp,emit_expr(aice[i]))
+';');
end;
end;
out;
ndefer("}");
-- compute all hot arguments into frame
if ~void(aice.fun.hot) then
-- beware the difference in indices!!!
loop i::=1.upto!(aice.asize-1);
if aice.fun.hot[i-1] then
ndefer(mangle(aice)+"->arg"+i+" = "
+cast(aice.fun.args[i-1],aice[i].tp,emit_expr(aice[i]))
+';');
comment("hot argument");
end;
end;
end;
if ~void(aice.tp) then
ndefer(s1+" = "+mangle(aice.fun)+'('+mangle(aice)+");");
res:=s1;
else
ndefer(mangle(aice.fun)+'('+mangle(aice)+");");
res:="0 /* No return value from iter call */";
end;
ndefer("if ("+mangle(aice)+"->state == -1) goto "+current_loop+";");
return res;
end;
emit_str_const(asc:AM_STR_CONST):STR is
res::="((STR) &"+mangle(asc)+')';
globals_c+"struct {\n";
globals_c+" OB_HEADER header;\n";
globals_c+" INT asize;\n";
globals_c+" CHAR arr_part["
+(asc.bval.length+1) -- +1 for Object Center bug
+"];\n } "+mangle(asc)+" = { ";
if prog.options.deterministic then
globals_c+"{ "+tag_for(prog.tp_builtin.str)+", -"+str_count+" }";
str_count:=str_count+1;
else
globals_c+tag_for(prog.tp_builtin.str);
end;
globals_c+", "+asc.bval.length+", \""+Cify(asc.bval)+"\" };\n";
strings_h+"\nextern STR "+mangle(asc)+';';
return res;
end;
emit_expr(arg:$AM_EXPR):STR pre ~void(arg) is
-- emit code for computing expr if necessary, and return handle
-- to the result.
typecase arg
when AM_LOCAL_EXPR then return mangle(arg);
when AM_ROUT_CALL_EXPR then return emit_rout_call(arg);
when AM_ITER_CALL_EXPR then return emit_iter_call(arg);
when AM_VOID_CONST then
assert ~void(arg.tp);
return default_init(arg.tp);
when AM_STR_CONST then return emit_str_const(arg);
when AM_BOOL_CONST then
if arg.val then return "TRUE" else return "FALSE" end;
when AM_INT_CONST then return arg.val.str;
when AM_CHAR_CONST then return "'"+Cify(arg.bval)+'\'';
when AM_FLT_CONST then return arg.val.str(8); -- two extra
when AM_FLTD_CONST then return arg.val.str(17); -- two extra
when AM_FLTX_CONST then
barf_at("FLTX literals not implemented yet",arg);
when AM_FLTDX_CONST then
barf_at("FLTDX literals not implemented yet",arg);
when AM_IF_EXPR then
res::=dec_local_comment(arg.tp,"local for :? test");
ndefer("if ("+emit_expr(arg.test)+") {");
in; ndefer(res+" = "+emit_expr(arg.if_true)+';'); out;
ndefer("} else {");
in; ndefer(res+" = "+emit_expr(arg.if_false)+';'); out;
ndefer("}");
return res;
when AM_NEW_EXPR then
res::=dec_local_comment(arg.tp_at,
"local for "+arg.tp_at.str+"::create");
if ~void(arg.asz) then
s2::=emit_expr(arg.asz);
ndefer(res+" = "+array_allocate(arg.tp_at,s2)+";");
ndefer(res+"->asize = "+s2+";");
else ndefer(res+" = "+allocate(arg.tp_at)+";");
end;
return res;
when AM_ATTR_EXPR then
s1::=emit_expr(arg.ob);
if chk_void and ~arg.ob.tp.is_value
and ~prog.options.null_segfaults then
ndefer("if ("+s1+"==NULL) {");
in;
runtime_error("Attr access "+arg.at.str+" of void",arg);
out;
ndefer("}");
end;
-- s1:=cast(arg.self_tp,arg.ob.tp,s1); -- NLP
-- if arg.self_tp.is_value then return s1+"."+mangle(arg.at); -- NLP
-- else return s1+"->"+mangle(arg.at); -- NLP
s2::=cast(arg.self_tp,arg.ob.tp,s1); -- NLP
if arg.self_tp.is_value then -- NLP
if s1.tail(1) = ")" and s2.tail(1) = ")" then -- NLP
return "("+mangle(arg.ob.tp)+"_blob="+s2+")."+mangle(arg.at); -- NLP
else -- NLP
return s2+"."+mangle(arg.at); -- NLP
end; -- NLP
else return s2+"->"+mangle(arg.at); -- NLP
end;
when AM_VATTR_ASSIGN_EXPR then
s1::=emit_expr(arg.ob);
s2::=mangle(arg.at);
s3::=emit_expr(arg.val);
res::=dec_local_comment(arg.tp,
"local for value type array assignment");
ndefer(res+" = "+s1+";");
ndefer(res+"."+s2+" = "+s3+";");
return res;
when AM_ARR_EXPR then
s1::=emit_expr(arg.ob);
s2::=emit_expr(arg.ind);
if ~arg.ob.tp.is_value then
if chk_void and ~prog.options.null_segfaults then
ndefer("if ("+s1+"==NULL) {");
in; runtime_error("Void array access",arg); out;
ndefer("}");
end;
if chk_bounds then
ndefer("if ("+s2+"<0||"+s2+">="+s1+"->asize) {");
in;
runtime_error("Index out of bounds",arg);
out;
ndefer("}");
end;
else
-- FIX HERE
ndefer("/* should do bounds check here */");
end;
if arg.ob.tp.is_value then return s1+".arr_part["+s2+"]";
else return s1+"->arr_part["+s2+"]";
end;
when AM_VARR_ASSIGN_EXPR then
s1::=emit_expr(arg.ob);
s2::=emit_expr(arg.ind);
s3::=emit_expr(arg.val);
res::=dec_local_comment(arg.tp,"local for value array assign");
-- FIX HERE
ndefer("/* should do dynamic bounds checking here */");
ndefer(res+" = "+s1+";");
ndefer(res+".arr_part["+s2+"] = "+s3+";");
return res;
when AM_EXT_CALL_EXPR then
arg_list:ARRAY{STR}:=emit_args(arg);
extern:STR:="extern ";
if ~void(arg.tp) then extern:=extern+mangle(arg.tp)+" ";
else extern:=extern+"void ";
end;
extern:=extern+arg.nm.str+"(";
res::=arg.nm.str+"(";
i:INT:=1; -- self is not passed to external routines
loop until!(i>=arg_list.asize);
if arg[i].tp.kind=TP_KIND::ref_tp then
arr:TP_CLASS:=prog.impl_tbl.impl_of(arg[i].tp).arr;
if ~void(arr) then
-- AREF{FOO} passed to external routines really
-- passes a pointer to the array portion.
extern:=extern+mangle(arr.params[0])+" []";
res:=res+"(("+arg_list[i]+"==NULL)?NULL:"
+arg_list[i]+"->arr_part)";
else
extern:=extern+mangle(arg[i].tp);
res:=res+arg_list[i];
end;
else
extern:=extern+mangle(arg[i].tp);
res:=res+arg_list[i];
end;
i:=i+1;
if i<arg_list.asize then
extern:=extern+", ";
res:=res+", ";
end;
end;
extern:=extern+");\n";
se:STR:=special_externs.get(arg.nm.str);
if ~void(se) then extern:=se+'\n'; end;
decs_h+extern;
return res+')';
when AM_GLOBAL_EXPR then return mangle(arg);
when AM_ARRAY_EXPR then
res::=dec_local_comment(arg.tp_at,
"local for array creation expression");
ndefer(res+" = "+array_allocate(arg.tp_at,arg.asize.str)+";");
ndefer(res+"->asize = "+arg.asize+";");
loop i::=arg.ind!;
ndefer(res+"->arr_part["+i+"] = "+emit_expr(arg[i])+';');
end;
return res;
when AM_IS_VOID_EXPR then
assert ~void(arg.arg);
arg_tp:$TP:=arg.arg.tp;
assert ~void(arg_tp);
if arg_tp.is_value and ~is_built_in_type(arg_tp) then
return value_void(arg_tp,emit_expr(arg.arg));
else
return "("+emit_expr(arg.arg)+"=="
+default_init(arg_tp)+")";
end;
when AM_STMT_EXPR then
if ~void(arg.stmts) then emit_code(arg.stmts); end;
if ~void(arg.expr) then return emit_expr(arg.expr);
else return void;
end;
when AM_EXCEPT_EXPR then
return cast(arg.tp,prog.tp_builtin.dollar_ob,"exception");
when AM_BND_CREATE_EXPR then
bnd_rout_creates:=bnd_rout_creates.push(arg);
res::=genlocal;
code_c+eol+' '+mangle(arg)+"_ob "+res+';';
ndefer(res+" = ("+mangle(arg)+"_ob) GC_malloc(sizeof(struct "
+mangle(arg)+"_ob_struct));");
ndefer(res+"->funcptr = "+mangle(arg)+';');
loop
i::=arg.ind!;
entry:STR;
idx::=arg.bnd_args[i];
if idx=0 then
entry:=cast(arg.fun.tp,arg[i].tp,emit_expr(arg[i]));
else
entry:=cast(arg.fun.args[idx-1],arg[i].tp,
emit_expr(arg[i]));
end;
ndefer(res+"->bound_arg"+i+" = "+entry+';');
end;
return "("+mangle(arg.tp)+") "+res;
when AM_BND_ROUT_CALL_EXPR then
tp::=arg.br.tp;
br::=dec_local(tp);
ndefer(br+" = "+emit_expr(arg.br)+';');
res::="(*("+br+"->funcptr))("+br;
arg_list::=emit_args(arg);
loop
i::=arg.ind!;
typecase tp when TP_ROUT then
res:=res+", "+cast(tp.args[i],arg[i].tp,arg_list.elt!);
end;
end;
return res+')';
-- from here on haven't been implemented yet.
when AM_BND_ITER_CALL_EXPR then
barf_at("bound iters not implemented yet",arg);
when AM_ARR_CONST then
barf_at("constant array literals not implemented yet",arg);
when AM_INTI_CONST then
barf_at("INTI literals not implemented yet",arg);
when AM_FLTI_CONST then
barf_at("FLTI constants not implemented yet",arg);
end; -- typecase
barf("Got to end of emit_expr");
return ""; -- because this routine must end with a return
end;
value_compare(tp:$TP,e1,e2:STR):STR pre tp.is_value is
-- expression for comparing contents of two value types
if is_built_in_type(tp) then return "("+e1+"=="+e2+")"; end;
aod:AM_OB_DEF:=prog.am_ob_def_for_tp(tp);
after_first:BOOL:=false;
res::="";
if ~void(aod.at) then
loop
p::=aod.at.pairs!;
key:STR:=mangle(p.t1);
if after_first then res:=res+"&&"; end;
if p.t2.is_value then
res:=res+value_compare(p.t2,e1+'.'+key,e2+'.'+key);
else
res:=res+'('+e1+'.'+key+"=="+e2+'.'+key+')';
end;
after_first:=true;
end;
end;
if ~void(aod.arr) then
loop
i::=0.for!(aod.asize);
if after_first then res:=res+"&&"; end;
res:=res+"("+e1+".arr_part["+i+"]=="+e2+".arr_part["+i+"])";
after_first:=true;
end;
end;
return res;
end;
value_void(tp:$TP,e:STR):STR pre tp.is_value is
-- expression for comparing value types to void (all zero elements)
if is_built_in_type(tp) then return "("+e+"==("+mangle(tp)+")0)"; end;
aod:AM_OB_DEF:=prog.am_ob_def_for_tp(tp);
after_first:BOOL:=false;
res::="";
if ~void(aod.at) then
loop
p::=aod.at.pairs!;
key:STR:=mangle(p.t1);
if after_first then res:=res+"&&"; end;
if p.t2.is_value then
-- res:=res+value_void(p.t2,e+'.'+key); -- NLP
if e.tail(1) = ")" then -- NLP
res:=res+value_void(p.t2,"("+mangle(tp)+"_blob="+e+")."+key); -- NLP
else -- NLP
res:=res+value_void(p.t2,e+'.'+key); -- NLP
end; -- NLP
else
-- res:=res+'('+e+'.'+key+"==("+mangle(p.t2)+")0)"; -- NLP
if e.tail(1) = ")" then -- NLP
res:=res+"(("+mangle(tp)+"_blob="+e+")."+key+"==("+mangle(p.t2)+")0)"; -- NLP
else -- NLP
res:=res+'('+e+'.'+key+"==("+mangle(p.t2)+")0)"; -- NLP
end; -- NLP
end;
after_first:=true;
end;
end;
if ~void(aod.arr) then
loop i::=0.for!(aod.asize);
if after_first then res:=res+"&&"; end;
res:=res+'('+e+".arr_part["+i
+"]==("+mangle(aod.arr)+")0)";
after_first:=true;
end;
end;
return res;
end;
force_mangle(ob:$OB, s:STR) is
-- see to it that a particular object gets a particular name.
-- if this is not possible, that is an error.
x:STR:=manglemap.get(ob);
if void(x) then
manglemap:=manglemap.insert(ob,s);
mangleset:=mangleset.insert(s);
elsif x/=s then
-- Already taken!
barf("Name "+s+" could not be assigned in back end");
end;
end;
remangle(ob:$OB, s:STR) is
-- rename object to have particular name. This differs
-- from force_mangle in that it doesn't remove the previous
-- name from mangleset.
x:STR:=manglemap.get(ob);
if void(x) or x/=s then
manglemap:=manglemap.insert(ob,s); -- this will overwrite
mangleset:=mangleset.insert(s);
end;
end;
unmangle(ob:$OB) is
-- remove object from mangling map, for instance, for local
-- variables after the body of a functions so that their
-- names may be reused.
s:STR:=manglemap.get(ob);
if ~void(s) then
manglemap:=manglemap.delete(ob);
mangleset:=mangleset.delete(s);
end;
end;
genlocal:STR is
-- generate a unique identifier used for intermediate results
res:STR;
loop
res:="local"+local_counter;
local_counter:=local_counter+1;
if ~mangleset.test(res) then break!; end;
end;
return res;
end;
genother:STR is
-- generate a unique identifier for anything
res:STR;
loop
res:="temp"+counter;
counter:=counter+1;
if ~mangleset.test(res) then break!; end;
end;
return res;
end;
private attr thisrout:FSET{$OB}; -- Set of mangled objects that can
-- safely be forgotten after the current routine has been
-- generated.
private start_mangling is thisrout:=#; local_counter:=0; end;
private end_mangling is
-- go through and unmangle anything which doesn't have to
-- be remembered outside of this function
loop unmangle(thisrout.elt!); end;
thisrout:=void;
end;
mangle(ob:$OB):STR pre ~void(ob) is
-- Generate unique id that C will be happy with for each unique $OB.
-- Uses s, if non-void, as a suggestion.
-- Truncates at 16 chars and then puts in number in rightmost part
-- to ensure is unique, if necessary.
-- Also drops any non-alphanumerics.
res::=manglemap.get(ob);
if void(res) then
s:STR;
typecase ob
when SIG then
s:=ob.tp.str+'_'+ob.name.str;
if ~void(ob.args) then
loop s:=s+'_'+ob.args.elt!.str; end;
end;
if ~void(ob.ret) then s:=s+'_'+ob.ret.str; end;
when AM_LOCAL_EXPR then
if ~void(ob.name) then s:=ob.name.str; end;
thisrout:=thisrout.insert(ob);
when TP_CLASS then s:=ob.str;
when TP_ROUT then s:=ob.str;
when AM_BND_CREATE_EXPR then s:="bound";
when STR then s:=ob;
when IDENT then s:=ob.str;
when AM_GLOBAL_EXPR then
s:="shared_"+ob.class_tp.str+'_'+ob.name.str;
when AM_LOOP_STMT then
s:="after_loop";
thisrout:=thisrout.insert(ob);
when AM_STR_CONST then s:=ob.bval;
else -- pick a default name
thisrout:=thisrout.insert(ob);
end;
if void(s) then res:="noname"+counter; counter:=counter+1;
else
tmp::=#FSTR; -- Use an FSTR for speed
loop c::=s.elt!;
case c
when 'a','b','c','d','e','f','g',
'h','i','j','k','l','m',
'n','o','p','q','r','s','t',
'u','v','w','x','y','z',
'0','1','2','3','4','5','6',
'7','8','9','_',
'A','B','C','D','E','F','G',
'H','I','J','K','L','M',
'N','O','P','Q','R','S','T',
'U','V','W','X','Y','Z' then
tmp:=tmp+c;
else -- don't put anything else in
end;
end;
-- make sure there's something left
if tmp.length = 0 then tmp := tmp+"name" end;
-- make sure it starts with a letter
case tmp[0]
when '0','1','2','3','4','5','6','7','8','9','_' then
tmp:=#FSTR+"S"+tmp;
else
end;
res:=tmp.str;
-- truncate if too long
if res.length>20 then res:=res.head(16); end;
-- make sure it's unique
loop while!(mangleset.test(res) or forbidden.test(res));
-- not unique, better mangle more
res:=res.head(16.min(res.length))+'_'+counter;
counter:=counter+1;
end;
end;
mangleset:=mangleset.insert(res);
assert mangleset.test(res);
manglemap:=manglemap.insert(ob,res);
end;
assert manglemap.test(ob);
assert mangleset.test(res);
return res;
end;
Cify(c:CHAR):STR is
-- return an escaped version of c suitable for C.
res::="";
case c
when 'a','b','c','d','e','f','g',
'h','i','j','k','l','m',
'n','o','p','q','r','s','t',
'u','v','w','x','y','z',
'0','1','2','3','4','5','6',
'7','8','9',
'A','B','C','D','E','F','G',
'H','I','J','K','L','M',
'N','O','P','Q','R','S','T',
'U','V','W','X','Y','Z',
'!','@','#','$','%','^','&',
'*','(',')','-','=','+',
'|',':',';','`','~','_',' ',
',','.','<','>','/','?','[',
']','{','}' then -- an acceptable character
res:=res+c;
--when '\a' then
-- res:=res+"\\a";
when '\b' then
res:=res+"\\b";
when '\f' then
res:=res+"\\f";
when '\n' then
res:=res+"\\n";
when '\r' then
res:=res+"\\r";
when '\t' then
res:=res+"\\t";
when '\v' then
res:=res+"\\v";
when '\\' then
res:=res+"\\\\";
when '\'' then
res:=res+"\\'";
when '\"' then
res:=res+"\\\"";
else -- must give octal
oc:STR:=c.int.octal_str;
oc:=oc.substring(2,oc.length-2);
res:=res+'\\'+oc;
end; -- case
return res;
end;
Cify(arg:STR):STR is
-- transform a string into a '\' escaped version suitable for C.
res::=#FSTR;
loop res:=res+Cify(arg.elt!);
end;
return res.str;
end;
enforce_tag(tp:$TP, tag:INT) is
-- See to it that a particular type receives a particular tag.
if tags.test(tp) then
if tags.get(tp)/=tag then
barf("Couldn't enforce tag for "+tp.str);
end;
end;
tags:=tags.insert(tp,tag);
end;
-- private
attr pos_tag_count:INT;
attr neg_tag_count:INT;
num_tag_for(tp:$TP):INT is
-- Numeric tag corresponding to a particular type. If not known,
-- make a new one.
tag:INT;
if ~tags.test(tp) then
if tp.is_value then
tag:= -neg_tag_count;
neg_tag_count:=neg_tag_count+1;
else tag:= pos_tag_count; pos_tag_count:=pos_tag_count+1;
end;
tags:=tags.insert(tp,tag);
else tag:=tags.get(tp);
end;
return tag;
end;
tag_for(tp:$TP):STR is
-- Expression corresponding to a particular type. If not known,
-- make a new one.
dummy:INT:=num_tag_for(tp); -- make sure gets entered into table
res::=mangle(tp)+"_tag";
forbid(res);
return res;
end;
end; -- BE
class BE_LEX is
private attr lex_state:INT;
-- 0 for default, 1 for in comment, 2 for in string
private attr buf:FSTR;
-- contents of the file being parsed
private attr pos:INT;
-- current read position
private attr name:STR;
-- Name of the file
create(s:STR):SAME is
res::=new;
res.name:=s;
f::=FILE::open_for_read(s);
if f.error then barf("Couldn't open system file "+s); end;
res.lex_state:=0;
res.buf:=f.fstr;
res.pos:=0;
return res;
end;
get_str:STR is
-- ignore comments and whitespace and read in a "-delimited string.
-- When the last string has been read, this returns void.
c:CHAR;
tmp::=#FSTR;
loop
until!(pos>=buf.size);
c:=buf[pos];
pos:=pos+1;
case lex_state
when 0 then
case c
when '-' then lex_state:=1;
when '\"' then lex_state:=2;
when '\n',' ','\t','\r','\\' then
else barf("Illegal character "+c.pretty
+" in input file "+name);
end;
when 1 then
case c
when '\n','\r' then lex_state:=0;
else
end;
when 2 then
case c
when '\"' then lex_state:=0; return tmp.str;
-- when '\\' then -- ignore backslashes -- NLP
else tmp:=tmp+c;
end;
else barf("Unknown lex state in back end");
end;
end;
return void;
end;
elt!:STR is
loop
s::=get_str;
if void(s) then quit;
else yield s;
end;
end;
end;
barf(msg:STR) is
#ERR + msg + '\n';
UNIX::exit(1);
end;
end; -- BE_LEX
class ITER_INLINE is
attr at_decs, when_first_seen, before, after:STR;
create(s1,s2,s3,s4:STR):SAME is
res::=new;
res.at_decs:=s1;
res.when_first_seen:=s2;
res.before:=s3;
res.after:=s4;
return res;
end;
end;