MODULE; IMPORT Wr, Thread, Fmt, Text, Stdio, IntIntTbl; IMPORT M3ID, M3CG, M3CG_Ops, Target, TargetMap; FROM M3CG IMPORT Name, ByteOffset, CallingConvention; FROM M3CG IMPORT ByteSize, Alignment, Frequency, RuntimeError; FROM M3CG IMPORT Var, Proc, Label, Sign, CompareOp, ConvertOp, AtomicOp; FROM M3CG IMPORT Type, ZType, AType, RType, IType, MType; FROM M3CG IMPORT MemoryOrder; TYPE (* stack data types *) ST = { Addr, Int32, Int64, Reel, LReel, XReel, Void, IType, RType, AType, Any, Missing, DontCare, Match }; VAR (* CONST after "begin_unit" call *) T_to_ST := ARRAY Type OF ST {ST.Void,..}; CONST ST_name = ARRAY ST OF TEXT { "Addr ", "Int32", "Int64", "Real ", "LReal ", "ExReal ", "Void ", "W,I ", "R,L,E ", "W,I,R,L,E ", "any ", "", "", "<=match " }; TYPE U = M3CG.T OBJECT clean_stores := FALSE; clean_jumps := FALSE; nested_calls := TRUE; nested_procs := FALSE; cur_line : INTEGER := 0; next_var := 1; next_proc := 1; next_scope := 1; n_errors := 0; proc_count := 0; block_count := 0; call_count := 0; top_of_stack := 0; in_init := 0; init_cursor := 0; note_error : M3CG_Ops.ErrorHandler := NIL; runtime : IntIntTbl.T := NIL; (* Name -> BOOL *) M3CG_Check
temps : IntIntTbl.T := NIL; (* Var -> line number
*) stack : ARRAY [0..50] OF Type; METHODS s_pop (s0, s1, s2, s3 := ST.DontCare) := Stack_Pop; s_push (t: Type) := Stack_Push; s_repush () := Stack_Repush; s_empty () := Stack_Empty; OVERRIDES set_error_handler := set_error_handler; begin_unit := begin_unit; end_unit := end_unit; set_source_line := set_source_line; set_runtime_proc := set_runtime_proc; set_runtime_hook := set_runtime_hook; get_runtime_hook := get_runtime_hook; bind_segment := bind_segment; declare_temp := declare_temp; free_temp := free_temp; begin_init := begin_init; end_init := end_init; init_int := init_int; init_proc := init_proc; init_label := init_label; init_var := init_var; init_offset := init_offset; init_chars := init_chars; init_float := init_float; begin_procedure := begin_procedure; end_procedure := end_procedure; begin_block := begin_block; end_block := end_block; note_procedure_origin := note_procedure_origin; set_label := set_label; jump := jump; if_true := if_true; if_false := if_false; if_compare := if_compare; case_jump := case_jump; exit_proc := exit_proc; load := load; store := store; load_address := load_address; load_indirect := load_indirect; store_indirect := store_indirect; load_nil := load_nil; load_integer := load_integer; load_float := load_float; compare := compare; add := add; subtract := subtract; multiply := multiply; divide := divide; div := div; mod := mod; negate := negate; abs := abs; max := max; min := min; cvt_int := cvt_int; cvt_float := cvt_float; set_union := set_union; set_difference := set_difference; set_intersection := set_intersection; set_sym_difference := set_sym_difference; set_member := set_member; set_compare := set_compare; set_range := set_range; set_singleton := set_singleton; not := not; and := and; or := or; xor := xor; shift := shift; shift_left := shift_left; shift_right := shift_right; rotate := rotate; rotate_left := rotate_left; rotate_right := rotate_right; widen := widen; chop := chop; extract := extract; extract_n := extract_n; extract_mn := extract_mn; insert := insert; insert_n := insert_n; insert_mn := insert_mn; swap := swap; pop := pop; copy := copy; copy_n := copy_n; zero := zero; zero_n := zero_n; loophole := loophole; abort := abort; check_nil := check_nil; check_lo := check_lo; check_hi := check_hi; check_range := check_range; check_index := check_index; check_eq := check_eq; add_offset := add_offset; index_address := index_address; start_call_direct := start_call_direct; call_direct := call_direct; start_call_indirect := start_call_indirect; call_indirect := call_indirect; pop_param := pop_param; pop_struct := pop_struct; pop_static_link := pop_static_link; load_procedure := load_procedure; load_static_link := load_static_link; store_ordered := store_ordered; load_ordered := load_ordered; exchange := exchange; compare_exchange := compare_exchange; fence := fence; fetch_and_op := fetch_and_op; END;----------------------------------------------- binary/ASCII conversion ---
VAR Ints := ARRAY [0..1023] OF TEXT { NIL, .. }; PROCEDURE--------------------------------------------------------- low level I/O ---Int (i: INTEGER): TEXT = BEGIN IF (FIRST (Ints) <= i) AND (i <= LAST (Ints)) THEN IF (Ints[i] = NIL) THEN Ints [i] := " " & Fmt.Int (i) END; RETURN Ints [i]; ELSE RETURN " " & Fmt.Int (i); END; END Int;
PROCEDURE-------------------------------------------------------- stack checking ---PutErr (u: U; a, b, c: TEXT := NIL) = BEGIN u.child.comment ("********* M3CG_Check ERROR *********** ", a, b, c); INC (u.n_errors); END PutErr;
PROCEDURE************** DEBUGGING ******** PROCEDURE SDump (self: U) = BEGIN self.child.comment (Stack_Get (self: U; depth: INTEGER): ST = VAR x := self.top_of_stack - depth - 1; BEGIN IF (FIRST (self.stack) <= x) AND (x <= LAST (self.stack)) THEN RETURN T_to_ST [self.stack [x]]; ELSE RETURN ST.Missing; END; END Stack_Get; PROCEDUREIsOK (need, got, prev: ST): BOOLEAN = BEGIN CASE need OF | ST.IType => RETURN (T_to_ST [FIRST (IType)] <= got) AND (got <= T_to_ST [LAST (IType)]); | ST.RType => RETURN (T_to_ST [FIRST (RType)] <= got) AND (got <= T_to_ST [LAST (RType)]); | ST.AType => RETURN (T_to_ST [FIRST (AType)] <= got) AND (got <= T_to_ST [LAST (AType)]); | ST.Any => RETURN (T_to_ST [FIRST (Type)] <= got) AND (got <= T_to_ST [LAST (Type)]); | ST.Void => RETURN (got = ST.Missing); | ST.DontCare => RETURN TRUE; | ST.Match => RETURN got = prev; ELSE RETURN (got = need); END; END IsOK; PROCEDUREST_Name (a, prev: ST): TEXT = BEGIN IF (a = ST.Match) THEN a := prev END; RETURN ST_name [a]; END ST_Name; PROCEDUREStack_Pop (self: U; a, b, c, d: ST) = VAR s0 := Stack_Get (self, 0); s1 := Stack_Get (self, 1); s2 := Stack_Get (self, 2); s3 := Stack_Get (self, 3); BEGIN IF IsOK (a, s0, a) AND IsOK (b, s1, a) AND IsOK (c, s2, b) AND IsOK (d, s3, c) THEN (* no error *) ELSE PutErr (self, "bad stack: expected [ ", ST_Name (a, a) & " " & ST_Name (b, a) & " " & ST_Name (c, b) & " " & ST_Name (d, c), " ] got [ " & ST_Name (s0, s0) & " " & ST_Name (s1, s0) & " " & ST_Name (s2, s1) & " " & ST_Name (s3, s2) & " ]"); END; IF (d # ST.DontCare) THEN DEC (self.top_of_stack, 4) ELSIF (c # ST.DontCare) THEN DEC (self.top_of_stack, 3) ELSIF (b # ST.DontCare) THEN DEC (self.top_of_stack, 2) ELSE (*a # ST.DontCare*) DEC (self.top_of_stack) END; IF (self.top_of_stack < 0) THEN self.top_of_stack := 0 END; END Stack_Pop; PROCEDUREStack_Push (self: U; t: Type) = BEGIN IF (self.top_of_stack <= LAST (self.stack)) THEN self.stack [self.top_of_stack] := t; ELSE PutErr (self, "stack overflow"); END; INC (self.top_of_stack); END Stack_Push; PROCEDUREStack_Repush (self: U) = BEGIN INC (self.top_of_stack); END Stack_Repush; PROCEDUREStack_Empty (self: U) = BEGIN IF (self.top_of_stack > 0) THEN PutErr (self, "non-empty stack: ", Stack_Dump (self)); self.top_of_stack := 0; END; END Stack_Empty;
****
, Stack_Dump (self));
END SDump;
***************************************
PROCEDURE---------------------------------------------------------------------------Stack_Dump (self: U): TEXT = VAR s := "[ "; BEGIN FOR i := 0 TO MIN (self.top_of_stack - 1, 4) DO s := s & ST_name [Stack_Get (self, i)]; END; IF (self.top_of_stack > 5) THEN s := s & "... "; END; s := s & "]"; RETURN s; END Stack_Dump;
PROCEDURE------------------------------------------------ READONLY configuration ---New (child: M3CG.T; clean_jumps, clean_stores: BOOLEAN; nested_calls, nested_procs: BOOLEAN): M3CG.T = BEGIN child.set_error_handler (CrashAndBurn); RETURN NEW (U, child := child, note_error := CrashAndBurn, runtime := NEW (IntIntTbl.Default).init (20), clean_jumps := clean_jumps, clean_stores := clean_stores, nested_calls := nested_calls, nested_procs := nested_procs ); END New; PROCEDURECrashAndBurn (msg: TEXT) = <*FATAL Wr.Failure, Thread.Alerted*> BEGIN Wr.PutText (Stdio.stdout, "Unhandled M3CG_Check error: " & msg); Wr.Flush (Stdio.stdout); Wr.Flush (Stdio.stderr); <*ASSERT FALSE*> END CrashAndBurn;
PROCEDURE----------------------------------------------------- compilation units ---set_error_handler (self: U; p: M3CG_Ops.ErrorHandler) = BEGIN self.note_error := p; self.child.set_error_handler (p); END set_error_handler;
PROCEDURE------------------------------------------------ debugging line numbers ---begin_unit (self: U; optimize : INTEGER) = (* called before any other method to initialize the compilation unit *) CONST IntMap = ARRAY [0..1] OF ST {ST.Int32, ST.Int64}; VAR ST_Int := IntMap[Target.Integer.bytes DIV 8]; BEGIN (* initialize the mapping of stack types *) T_to_ST := ARRAY Type OF ST {ST_Int, ST_Int, ST_Int, ST_Int, (* Word8, Int8, Word16, Int16 *) ST_Int, ST_Int, ST.Int64, ST.Int64, (* Word32, Int32, Word64, Int64 *) ST.Reel, ST.LReel, ST.XReel, (* Reel, LReel, XReel *) ST.Addr, ST.Addr, (* Addr, Struct *) ST.Void}; self.s_empty (); self.child.begin_unit (optimize); END begin_unit; PROCEDUREend_unit (self: U) = (* called after all other methods to finalize the unit and write the resulting object *) BEGIN self.s_empty (); self.child.end_unit (); IF (self.n_errors <= 0) THEN (* ok *) ELSIF (self.n_errors = 1) THEN self.note_error ("1 code generation error"); ELSE (*self.n_errors > 1 *) self.note_error (Int (self.n_errors) & " code generation errors"); END; END end_unit;
PROCEDURE--------------------------------------------------------- runtime hooks ---set_source_line (self: U; line: INTEGER) = BEGIN self.cur_line := line; self.child.set_source_line (line); END set_source_line;
PROCEDURE------------------------------------------------- variable declarations ---set_runtime_proc (self: U; n: Name; p: Proc) = BEGIN CheckProc (self, p); IF self.runtime.put (n, 0) THEN PutErr (self, "redefined runtime proc: ", M3ID.ToText (n)); END; self.child.set_runtime_proc (n, p); END set_runtime_proc; PROCEDUREset_runtime_hook (self: U; n: Name; v: Var; o: ByteOffset) = BEGIN CheckVar (self, v); IF self.runtime.put (n, 0) THEN PutErr (self, "redefined runtime hook: ", M3ID.ToText (n)); END; self.child.set_runtime_hook (n, v, o); END set_runtime_hook; PROCEDUREget_runtime_hook (self: U; n: Name; VAR p: Proc; VAR v: Var; VAR o: ByteOffset) = VAR i: INTEGER; BEGIN IF NOT self.runtime.get (n, i) THEN PutErr (self, "undefined runtime hook: ", M3ID.ToText (n)); END; self.child.get_runtime_hook (n, p, v, o); END get_runtime_hook;
PROCEDURECheckVar (self: U; v: Var) = BEGIN IF (v = NIL) THEN PutErr (self, "NIL variable"); END; END CheckVar; PROCEDUREbind_segment (self: U; seg: Var; s: ByteSize; a: Alignment; t: Type; exported, inited: BOOLEAN) = BEGIN CheckVar (self, seg); self.child.bind_segment (seg, s, a, t, exported, inited); END bind_segment; PROCEDUREdeclare_temp (self: U; s: ByteSize; a: Alignment; t: Type; in_memory:BOOLEAN): Var = VAR v: Var; BEGIN
IF (self.temps = NIL) THEN self.temps := NEW (IntIntTbl.Default).init (); END;
v := self.child.declare_temp (s, a, t, in_memory);
IF self.temps.put (v, self.cur_line) THEN PutErr (self,
temporary reused while live!
); END;
RETURN v; END declare_temp; PROCEDUREfree_temp (self: U; v: Var) = (* VAR line: INTEGER; *) BEGIN CheckVar (self, v);
IF (self.temps = NIL) THEN self.temps := NEW (IntIntTbl.Default).init (); END; IF NOT self.temps.delete (v, line) THEN PutErr (self,
temp freed twice
); END;
self.child.free_temp (v); END free_temp;---------------------------------------- static variable initialization ---
PROCEDURE------------------------------------------------------------ procedures ---begin_init (self: U; v: Var) = BEGIN CheckVar (self, v); IF (self.in_init > 0) THEN PutErr (self, "nested static initialization"); END; INC (self.in_init); self.init_cursor := 0; self.child.begin_init (v); END begin_init; PROCEDUREend_init (self: U; v: Var) = BEGIN CheckVar (self, v); IF (self.in_init > 0) THEN DEC (self.in_init); self.init_cursor := 0; ELSE PutErr (self, "missing begin_init"); END; self.child.end_init (v); END end_init; PROCEDUREDoInit (self: U; o: ByteOffset; s: ByteSize) = BEGIN IF (self.in_init <= 0) THEN PutErr (self, "missing begin_init") END; IF (o >= self.init_cursor) THEN self.init_cursor := o + s; ELSE PutErr (self, "decreasing offsets"); END; END DoInit; PROCEDUREinit_int (self: U; o: ByteOffset; READONLY value: Target.Int; t: Type) = BEGIN DoInit (self, o, TargetMap.CG_Bytes[t]); self.child.init_int (o, value, t); END init_int; PROCEDUREinit_proc (self: U; o: ByteOffset; value: Proc) = BEGIN DoInit (self, o, Target.Address.bytes); self.child.init_proc (o, value); END init_proc; PROCEDUREinit_label (self: U; o: ByteOffset; value: Label) = BEGIN DoInit (self, o, Target.Address.bytes); self.child.init_label (o, value); END init_label; PROCEDUREinit_var (self: U; o: ByteOffset; value: Var; bias: ByteOffset) = BEGIN DoInit (self, o, Target.Address.bytes); self.child.init_var (o, value, bias); END init_var; PROCEDUREinit_offset (self: U; o: ByteOffset; value: Var) = BEGIN DoInit (self, o, Target.Integer.bytes); self.child.init_offset (o, value); END init_offset; PROCEDUREinit_chars (self: U; o: ByteOffset; value: TEXT) = BEGIN DoInit (self, o, Text.Length (value) * Target.Char.bytes); self.child.init_chars (o, value); END init_chars; PROCEDUREinit_float (self: U; o: ByteOffset; READONLY f: Target.Float) = BEGIN DoInit (self, o, TargetMap.Float_types[f.pre].bytes); self.child.init_float (o, f); END init_float;
PROCEDURECheckProc (self: U; p: Proc) = BEGIN IF (p = NIL) THEN PutErr (self, "NIL procedure"); END; END CheckProc; PROCEDUREbegin_procedure (self: U; p: Proc) = BEGIN CheckProc (self, p); IF (self.proc_count > 0) AND (NOT self.nested_procs) THEN PutErr (self, "nested procedure declaration"); END; INC (self.proc_count); self.child.begin_procedure (p); END begin_procedure; PROCEDUREend_procedure (self: U; p: Proc) = BEGIN CheckProc (self, p); IF (self.proc_count > 0) THEN DEC (self.proc_count); ELSE PutErr (self, "missing begin_procedure"); END; IF (self.block_count > 0) AND (NOT self.nested_procs) THEN PutErr (self, "missing end_blocks: ", Int (self.block_count)); self.block_count := 0; END; self.s_empty ();
IF (self.temps # NIL) THEN VAR it := self.temps.iterate (); k: REFANY; line: INTEGER; BEGIN WHILE it.next (tag, line) DO PutErr (self,
temp not freed, created on line
, Int (line)); END; END; END;
self.child.end_procedure (p); END end_procedure; PROCEDURE------------------------------------------------------------ statements ---begin_block (self: U) = (* marks the beginning of a nested anonymous block *) BEGIN IF (self.proc_count <= 0) THEN PutErr (self, "begin_block not in procedure"); END; self.s_empty (); INC (self.block_count); self.child.begin_block (); END begin_block; PROCEDUREend_block (self: U) = (* marks the ending of a nested anonymous block *) BEGIN IF (self.block_count > 0) THEN DEC (self.block_count); ELSE PutErr (self, "missing begin_block"); END; self.s_empty (); self.child.end_block (); END end_block; PROCEDUREnote_procedure_origin (self: U; p: Proc) = BEGIN CheckProc (self, p); self.s_empty (); self.child.note_procedure_origin (p); END note_procedure_origin;
PROCEDURE------------------------------------------------------------ load/store ---CheckLabel (self: U; l: Label) = BEGIN IF (l < 0) (*OR (self.next_label <= l)*) THEN PutErr (self, "undefined label: ", Int (l)); END; END CheckLabel; PROCEDUREset_label (self: U; l: Label; barrier: BOOLEAN) = (* define 'l' to be at the current pc *) BEGIN IF (self.clean_jumps) THEN self.s_empty () END; CheckLabel (self, l); self.child.set_label (l, barrier); END set_label; PROCEDUREjump (self: U; l: Label) = (* GOTO l *) BEGIN IF (self.clean_jumps) THEN self.s_empty () END; CheckLabel (self, l); self.child.jump (l); END jump; PROCEDUREif_true (self: U; t: IType; l: Label; f: Frequency) = (* IF (s0.t # 0) GOTO l ; pop *) BEGIN self.s_pop (T_to_ST[t]); IF (self.clean_jumps) THEN self.s_empty () END; CheckLabel (self, l); self.child.if_true (t, l, f); END if_true; PROCEDUREif_false (self: U; t: IType; l: Label; f: Frequency) = (* IF (s0.t = 0) GOTO l ; pop *) BEGIN self.s_pop (T_to_ST[t]); IF (self.clean_jumps) THEN self.s_empty () END; CheckLabel (self, l); self.child.if_false (t, l, f); END if_false; PROCEDUREif_compare (self: U; t: ZType; op: CompareOp; l: Label; f: Frequency) = (* IF (s1.t op s0.t) GOTO l ; pop(2) *) BEGIN self.s_pop (T_to_ST [t], ST.Match); IF (self.clean_jumps) THEN self.s_empty () END; CheckLabel (self, l); self.child.if_compare (t, op, l, f); END if_compare; PROCEDUREcase_jump (self: U; t: IType; READONLY labels: ARRAY OF Label) = (* "GOTO labels[s0.t] ; pop" with no range checking on s0.t *) BEGIN self.s_pop (T_to_ST[t]); IF (self.clean_jumps) THEN self.s_empty () END; FOR i := FIRST (labels) TO LAST (labels) DO CheckLabel (self, labels [i]); END; self.child.case_jump (t, labels); END case_jump; PROCEDUREexit_proc (self: U; t: Type) = (* Returns s0.t if the stack is non-empty, otherwise returns no value. *) BEGIN IF (t # Type.Void) THEN self.s_pop (T_to_ST [t]) END; self.s_empty (); self.child.exit_proc (t); END exit_proc;
TYPE ZMap = ARRAY ZType OF BOOLEAN; TYPE MMap = ARRAY MType OF BOOLEAN; CONST LegalLoad = ARRAY MType, ZType OF BOOLEAN { (* W32 I32 W64 I64 Reel LReel XReel Addr *)
Word8
ZMap{ TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Int8
ZMap{ TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Word16
ZMap{ TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Int16
ZMap{ TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Word32
ZMap{ TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Int32
ZMap{ TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Word64
ZMap{ FALSE,FALSE,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Int64
ZMap{ FALSE,FALSE,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Reel
ZMap{ FALSE,FALSE,FALSE,FALSE,TRUE ,FALSE,FALSE,FALSE },
LReel
ZMap{ FALSE,FALSE,FALSE,FALSE,FALSE,TRUE ,FALSE,FALSE },
XReel
ZMap{ FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE ,FALSE },
Addr
ZMap{ FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE } }; CONST LegalStore = ARRAY ZType, MType OF BOOLEAN { (* Word8 Int8 W16 I16 W32 I32 W64 I64 Reel LReel XReel Addr *)
Word32
MMap{ TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE },
Int32
MMap{ TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE },
Word64
MMap{ TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Int64
MMap{ TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,TRUE ,FALSE,FALSE,FALSE,FALSE },
Reel
MMap{ FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE ,FALSE,FALSE,FALSE },
LReel
MMap{ FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE ,FALSE,FALSE },
XReel
MMap{ FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE ,FALSE },
Addr
MMap{ FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE } }; PROCEDURE-------------------------------------------------------------- literals ---load (self: U; v: Var; o: ByteOffset; t: MType; u: ZType) = BEGIN CheckVar (self, v); IF NOT LegalLoad [t, u] THEN PutErr (self, "illegal load conversion"); END; self.s_push (u); self.child.load (v, o, t, u); END load; PROCEDUREstore (self: U; v: Var; o: ByteOffset; t: ZType; u: MType) = BEGIN CheckVar (self, v); IF NOT LegalStore [t, u] THEN PutErr (self, "illegal store conversion"); END; self.s_pop (T_to_ST [t]); IF (self.clean_stores) THEN self.s_empty () END; self.child.store (v, o, t, u); END store; PROCEDUREload_address (self: U; v: Var; o: ByteOffset) = BEGIN CheckVar (self, v); self.s_push (Type.Addr); self.child.load_address (v, o); END load_address; PROCEDUREload_indirect (self: U; o: ByteOffset; t: MType; u: ZType) = BEGIN IF NOT LegalLoad [t, u] THEN PutErr (self, "illegal load conversion"); END; self.s_pop (ST.Addr); self.s_push (u); self.child.load_indirect (o, t, u); END load_indirect; PROCEDUREstore_indirect (self: U; o: ByteOffset; t: ZType; u: MType) = BEGIN IF NOT LegalStore [t, u] THEN PutErr (self, "illegal store conversion"); END; self.s_pop (T_to_ST [t], ST.Addr); IF (self.clean_stores) THEN self.s_empty () END; self.child.store_indirect (o, t, u); END store_indirect;
PROCEDURE------------------------------------------------------------ arithmetic ---load_nil (self: U) = (* push ; s0.A := a *) BEGIN self.s_push (Type.Addr); self.child.load_nil (); END load_nil; PROCEDUREload_integer (self: U; t: IType; READONLY i: Target.Int) = (* push ; s0.t := i *) BEGIN self.s_push (t); self.child.load_integer (t, i); END load_integer; PROCEDUREload_float (self: U; t: RType; READONLY f: Target.Float) = (* push ; s0.t := f *) CONST FType = ARRAY Target.Precision OF RType { Type.Reel, Type.LReel, Type.XReel }; BEGIN IF t # FType [f.pre] THEN PutErr (self, "floating-point literal doesn't match type"); END; self.s_push (t); self.child.load_float (t, f); END load_float;
PROCEDURE------------------------------------------------------------------ sets ---Binary (self: U; lhs, rhs: Type) = (* s1.lhs := s1.rhs 'op' s0.rhs ; pop *) BEGIN self.s_pop (T_to_ST [rhs], ST.Match); self.s_push (lhs); END Binary; PROCEDUREUnary (self: U; lhs, rhs: Type) = (* s1.lhs := 'op' (s1.rhs) *) BEGIN self.s_pop (T_to_ST [rhs]); self.s_push (lhs); END Unary; PROCEDUREcompare (self: U; t: ZType; u: IType; op: CompareOp) = (* s1.u := (s1.t op s0.t) ; pop *) BEGIN Binary (self, u, t); self.child.compare (t, u, op); END compare; PROCEDUREadd (self: U; t: AType) = (* s1.t := s1.t + s0.t ; pop *) BEGIN Binary (self, t, t); self.child.add (t); END add; PROCEDUREsubtract (self: U; t: AType) = (* s1.t := s1.t - s0.t ; pop *) BEGIN Binary (self, t, t); self.child.subtract (t); END subtract; PROCEDUREmultiply (self: U; t: AType) = (* s1.t := s1.t * s0.t ; pop *) BEGIN Binary (self, t, t); self.child.multiply (t); END multiply; PROCEDUREdivide (self: U; t: RType) = (* s1.t := s1.t / s0.t ; pop *) BEGIN Binary (self, t, t); self.child.divide (t); END divide; PROCEDUREdiv (self: U; t: IType; a, b: Sign) = (* s1.t := s1.t DIV s0.t ; pop *) BEGIN self.s_pop (T_to_ST [t], ST.Match); self.s_push (t); self.child.div (t, a, b); END div; PROCEDUREmod (self: U; t: IType; a, b: Sign) = (* s1.t := s1.t MOD s0.t ; pop *) BEGIN self.s_pop (T_to_ST [t], ST.Match); self.s_push (t); self.child.mod (t, a, b); END mod; PROCEDUREnegate (self: U; t: AType) = (* s0.t := - s0.t *) BEGIN Unary (self, t, t); self.child.negate (t); END negate; PROCEDUREabs (self: U; t: AType) = (* s0.t := ABS (s0.t) (noop on Words) *) BEGIN Unary (self, t, t); self.child.abs (t); END abs; PROCEDUREmax (self: U; t: ZType) = (* s1.t := MAX (s1.t, s0.t) ; pop *) BEGIN Binary (self, t, t); self.child.max (t); END max; PROCEDUREmin (self: U; t: ZType) = (* s1.t := MIN (s1.t, s0.t) ; pop *) BEGIN Binary (self, t, t); self.child.min (t); END min; PROCEDUREcvt_int (self: U; t: RType; u: IType; op: ConvertOp) = (* s0.u := op (s0.t) *) BEGIN Unary (self, u, t); self.child.cvt_int (t, u, op); END cvt_int; PROCEDUREcvt_float (self: U; t: AType; u: RType) = (* s0.u := FLOAT (s0.t, u) *) BEGIN Unary (self, u, t); self.child.cvt_float (t, u); END cvt_float;
PROCEDURE------------------------------------------------- Word.T bit operations ---set_union (self: U; s: ByteSize) = (* s2.B := s1.B + s0.B ; pop(3) *) BEGIN self.s_pop (ST.Addr, ST.Addr, ST.Addr); self.child.set_union (s); END set_union; PROCEDUREset_difference (self: U; s: ByteSize) = (* s2.B := s1.B - s0.B ; pop(3) *) BEGIN self.s_pop (ST.Addr, ST.Addr, ST.Addr); self.child.set_difference (s); END set_difference; PROCEDUREset_intersection (self: U; s: ByteSize) = (* s2.B := s1.B * s0.B ; pop(3) *) BEGIN self.s_pop (ST.Addr, ST.Addr, ST.Addr); self.child.set_intersection (s); END set_intersection; PROCEDUREset_sym_difference (self: U; s: ByteSize) = (* s2.B := s1.B / s0.B ; pop(3) *) BEGIN self.s_pop (ST.Addr, ST.Addr, ST.Addr); self.child.set_sym_difference (s); END set_sym_difference; PROCEDUREset_member (self: U; s: ByteSize; t: IType) = (* s1.t := (s0.t IN s1.B) ; pop *) BEGIN self.s_pop (T_to_ST[t], ST.Addr); self.s_push (t); self.child.set_member (s, t); END set_member; PROCEDUREset_compare (self: U; s: ByteSize; op: CompareOp; t: IType) = (* s1.t := (s1.B op s0.B) ; pop *) BEGIN Binary (self, t, Type.Addr); self.child.set_compare (s, op, t); END set_compare; PROCEDUREset_range (self: U; s: ByteSize; t: IType) = (* s2.A [s1.t .. s0.t] := 1's; pop(3)*) BEGIN self.s_pop (T_to_ST[t], T_to_ST[t], ST.Addr); self.child.set_range (s, t); END set_range; PROCEDUREset_singleton (self: U; s: ByteSize; t: IType) = (* s1.A [s0.t] := 1; pop(2) *) BEGIN self.s_pop (T_to_ST[t], ST.Addr); self.child.set_singleton (s, t); END set_singleton;
PROCEDURE------------------------------------------------ misc. stack/memory ops ---not (self: U; t: IType) = (* s0.t := Word.Not (s0.t) *) BEGIN Unary (self, t, t); self.child.not (t); END not; PROCEDUREand (self: U; t: IType) = (* s1.t := Word.And (s1.t, s0.t) ; pop *) BEGIN Binary (self, t, t); self.child.and (t); END and; PROCEDUREor (self: U; t: IType) = (* s1.t := Word.Or (s1.t, s0.t) ; pop *) BEGIN Binary (self, t, t); self.child.or (t); END or; PROCEDURExor (self: U; t: IType) = (* s1.t := Word.Xor (s1.t, s0.t) ; pop *) BEGIN Binary (self, t, t); self.child.xor (t); END xor; PROCEDUREshift (self: U; t: IType) = (* s1.t := Word.Shift (s1.t, s0.t) ; pop *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.shift (t); END shift; PROCEDUREshift_left (self: U; t: IType) = (* s1.t := Word.Shift (s1.t, s0.t) ; pop *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.shift_left (t); END shift_left; PROCEDUREshift_right (self: U; t: IType) = (* s1.t := Word.Shift (s1.t, -s0.t) ; pop *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.shift_right (t); END shift_right; PROCEDURErotate (self: U; t: IType) = (* s1.t := Word.Rotate (s1.t, s0.t) ; pop *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.rotate (t); END rotate; PROCEDURErotate_left (self: U; t: IType) = (* s1.t := Word.Rotate (s1.t, s0.t) ; pop *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.rotate_left (t); END rotate_left; PROCEDURErotate_right (self: U; t: IType) = (* s1.t := Word.Rotate (s1.t, -s0.t) ; pop *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.rotate_right (t); END rotate_right; PROCEDUREwiden (self: U; sign: BOOLEAN) = (* s0.I64 := s0.I32; IF sign THEN Sign-extend s0; *) BEGIN self.s_pop (ST.Int32); self.s_push (Type.Int64); self.child.widen (sign); END widen; PROCEDUREchop (self: U) = (* s0.I32 := Word.And (s0.I64, 16_ffffffff); *) BEGIN self.s_pop (ST.Int64); self.s_push (Type.Int32); self.child.chop (); END chop; PROCEDUREextract (self: U; t: IType; sign: BOOLEAN) = (* s2.t := Word.Extract(s2.t, s1.t, s0.t); IF sign THEN SignExtend s2 END; pop(2) *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.extract (t, sign); END extract; PROCEDUREextract_n (self: U; t: IType; sign: BOOLEAN; n: INTEGER) = (* s1.t := Word.Extract(s1.t, s0.t, n); IF sign THEN SignExtend s1 END; pop(1) *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST [t]); self.s_push (t); self.child.extract_n (t, sign, n); END extract_n; PROCEDUREextract_mn (self: U; t: IType; sign: BOOLEAN; m, n: INTEGER) = (* s0.t := Word.Extract(s0.t, m, n); IF sign THEN SignExtend s0 END; *) BEGIN Unary (self, t, t); self.child.extract_mn (t, sign, m, n); END extract_mn; PROCEDUREinsert (self: U; t: IType) = (* s3.t := Word.Insert (s3.t, s2.t, s1.t, s0.t) ; pop(3) *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST[Target.Integer.cg_type], T_to_ST[t], T_to_ST[t]); self.s_push (t); self.child.insert (t); END insert; PROCEDUREinsert_n (self: U; t: IType; n: INTEGER) = (* s2.t := Word.Insert (s2.t, s1.t, s0.t, n) ; pop(2) *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST[t], T_to_ST[t]); self.s_push (t); self.child.insert_n (t, n); END insert_n; PROCEDUREinsert_mn (self: U; t: IType; m, n: INTEGER) = (* s1.t := Word.Insert (s1.t, s0.t, m, n) ; pop(2) *) BEGIN self.s_pop (T_to_ST[Target.Integer.cg_type], T_to_ST[t]); self.s_push (t); self.child.insert_mn (t, m, n); END insert_mn;
PROCEDURE----------------------------------------------------------- conversions ---swap (self: U; a, b: Type) = (* tmp := s1 ; s1 := s0 ; s0 := tmp *) BEGIN self.s_pop (T_to_ST [b], T_to_ST [a]); self.s_push (b); self.s_push (a); self.child.swap (a, b); END swap; PROCEDUREpop (self: U; t: Type) = (* pop(1) (i.e. discard s0) *) BEGIN self.s_pop (T_to_ST [t]); self.child.pop (t); END pop; PROCEDUREcopy_n (self: U; u: IType; t: MType; overlap: BOOLEAN) = (* Mem[s2.A:s0.t] := Mem[s1.A:s0.t]; pop(3)*) BEGIN self.s_pop (T_to_ST[u], ST.Addr, ST.Addr); self.child.copy_n (u, t, overlap); END copy_n; PROCEDUREcopy (self: U; n: INTEGER; t: MType; overlap: BOOLEAN) = (* Mem[s2.A:sz] := Mem[s1.A:sz]; pop(2)*) BEGIN self.s_pop (ST.Addr, ST.Addr); self.child.copy (n, t, overlap); END copy; PROCEDUREzero_n (self: U; u: IType; t: MType) = (* Mem[s1.A:s0.u] := 0; pop(2) *) BEGIN self.s_pop (T_to_ST[u], ST.Addr); self.child.zero_n (u, t); END zero_n; PROCEDUREzero (self: U; n: INTEGER; t: MType) = (* Mem[s1.A:sz] := 0; pop(1) *) BEGIN self.s_pop (ST.Addr); self.child.zero (n, t); END zero;
PROCEDURE------------------------------------------------ traps & runtime checks ---loophole (self: U; from, two: ZType) = (* s0.to := LOOPHOLE(s0.from, to) *) BEGIN self.s_pop (T_to_ST [from]); self.s_push (two); self.child.loophole (from, two); END loophole;
PROCEDURE---------------------------------------------------- address arithmetic ---abort (self: U; code: RuntimeError) = BEGIN self.s_empty (); self.child.abort (code); END abort; PROCEDUREcheck_nil (self: U; code: RuntimeError) = (* IF (s0.A = NIL) THEN abort(code) *) BEGIN self.s_pop (ST.Addr); self.s_push (Type.Addr); self.child.check_nil (code); END check_nil; PROCEDUREcheck_lo (self: U; t: IType; READONLY i: Target.Int; code: RuntimeError) = (* IF (s0.t < i) THEN abort(code) *) BEGIN Unary (self, t, t); self.child.check_lo (t, i, code); END check_lo; PROCEDUREcheck_hi (self: U; t: IType; READONLY i: Target.Int; code: RuntimeError) = (* IF (i < s0.t) THEN abort(code) *) BEGIN Unary (self, t, t); self.child.check_hi (t, i, code); END check_hi; PROCEDUREcheck_range (self: U; t: IType; READONLY a, b: Target.Int; code: RuntimeError) = (* IF (s0.t < a) OR (b < s0.t) THEN abort(code) *) BEGIN Unary (self, t, t); self.child.check_range (t, a, b, code); END check_range; PROCEDUREcheck_index (self: U; t: IType; code: RuntimeError) = (* IF NOT (0 <= s1.t < s0.t) THEN abort(code) END; pop *) BEGIN Binary (self, t, t); self.child.check_index (t, code); END check_index; PROCEDUREcheck_eq (self: U; t: IType; code: RuntimeError) = (* IF (s0.I # s1.I) THEN abort(code); Pop (2) *) BEGIN self.s_pop (T_to_ST[t], T_to_ST[t]); self.child.check_eq (t, code); END check_eq;
PROCEDURE------------------------------------------------------- procedure calls ---add_offset (self: U; i: INTEGER) = (* s0.A := s0.A + i *) BEGIN self.s_pop (ST.Addr); self.s_push (Type.Addr); self.child.add_offset (i); END add_offset; PROCEDUREindex_address (self: U; t: IType; size: INTEGER) = (* s1.A := s1.A + s0.t * size ; pop *) BEGIN self.s_pop (T_to_ST[t], ST.Addr); self.s_push (Type.Addr); self.child.index_address (t, size); END index_address;
PROCEDURE------------------------------------------- procedure and closure types ---start_call_direct (self: U; p: Proc; lev: INTEGER; t: Type) = (* begin a procedure call to a procedure at static level 'lev'. *) BEGIN CheckProc (self, p); IF (self.clean_jumps) THEN self.s_empty () END; IF (self.call_count > 0) AND (NOT self.nested_calls) THEN PutErr (self, "nested procedure call"); END; INC (self.call_count); self.child.start_call_direct (p, lev, t); END start_call_direct; PROCEDUREstart_call_indirect (self: U; t: Type; cc: CallingConvention) = (* begin a procedure call to a procedure at static level 'lev'. *) BEGIN IF (self.clean_jumps) THEN self.s_empty () END; IF (self.call_count > 0) AND (NOT self.nested_calls) THEN PutErr (self, "nested procedure call"); END; INC (self.call_count); self.child.start_call_indirect (t, cc); END start_call_indirect; PROCEDUREpop_param (self: U; t: MType) = (* pop s0 and make it the "next" parameter in the current call *) BEGIN IF (self.call_count <= 0) THEN PutErr (self, "missing start_call") END; self.s_pop (T_to_ST [t]); IF (self.clean_stores) THEN self.s_empty () END; self.child.pop_param (t); END pop_param; PROCEDUREpop_struct (self: U; s: ByteSize; a: Alignment) = (* pop s0 and make it the "next" parameter in the current call *) BEGIN IF (self.call_count <= 0) THEN PutErr (self, "missing start_call") END; self.s_pop (ST.Addr); IF (self.clean_stores) THEN self.s_empty () END; self.child.pop_struct (s, a); END pop_struct; PROCEDUREpop_static_link (self: U) = BEGIN IF (self.call_count <= 0) THEN PutErr (self, "missing start_call") END; self.s_pop (ST.Addr); IF (self.clean_stores) THEN self.s_empty () END; self.child.pop_static_link (); END pop_static_link; PROCEDUREDoCall (self: U) = BEGIN IF (self.clean_jumps) THEN self.s_empty () END; IF (self.call_count > 0) THEN DEC (self.call_count); ELSE PutErr (self, "missing start_call"); END; END DoCall; PROCEDUREcall_direct (self: U; p: Proc; t: Type) = (* call the procedure identified by block b. The procedure returns a value of type t. *) BEGIN CheckProc (self, p); DoCall (self); IF (t # Type.Void) THEN self.s_push (t) END; self.child.call_direct (p, t); END call_direct; PROCEDUREcall_indirect (self: U; t: Type; cc: CallingConvention) = (* call the procedure whose address is in s0.A and pop s0. The procedure returns a value of type t. *) BEGIN self.s_pop (ST.Addr); DoCall (self); IF (t # Type.Void) THEN self.s_push (t) END; self.child.call_indirect (t, cc); END call_indirect;
PROCEDURE--------------------------------------------------------------- atomics ---load_procedure (self: U; p: Proc) = (* push; s0.A := ADDR (p's body) *) BEGIN CheckProc (self, p); self.s_push (Type.Addr); self.child.load_procedure (p); END load_procedure; PROCEDUREload_static_link (self: U; p: Proc) = (* push; s0.A := (static link needed to call p, NIL for top-level procs) *) BEGIN CheckProc (self, p); self.s_push (Type.Addr); self.child.load_static_link (p); END load_static_link;
PROCEDUREstore_ordered (self: U; z: ZType; t: MType; order: MemoryOrder) = BEGIN IF NOT LegalStore [z, t] THEN PutErr (self, "illegal store conversion"); END; CASE order OF | MemoryOrder.Acquire, MemoryOrder.AcquireRelease => PutErr (self, "illegal store memory order"); ELSE (* ok *) END; self.s_pop (T_to_ST [z], ST.Addr); IF (self.clean_stores) THEN self.s_empty () END; self.child.store_ordered (z, t, order); END store_ordered; PROCEDUREload_ordered (self: U; t: MType; z: ZType; order: MemoryOrder) = BEGIN IF NOT LegalLoad [t, z] THEN PutErr (self, "illegal load conversion"); END; CASE order OF | MemoryOrder.Release, MemoryOrder.AcquireRelease => PutErr (self, "illegal load memory order"); ELSE (* ok *) END; self.s_pop (ST.Addr); self.s_push (z); self.child.load_ordered (t, z, order); END load_ordered; PROCEDUREexchange (self: U; m: MType; z: ZType; order: MemoryOrder) = BEGIN IF NOT LegalStore [z, m] THEN PutErr (self, "illegal store conversion"); END; IF NOT LegalLoad [m, z] THEN PutErr (self, "illegal load conversion"); END; self.s_pop (T_to_ST [z], ST.Addr); self.s_push (z); self.child.exchange (m, z, order); END exchange; PROCEDUREcompare_exchange (self: U; m: MType; z: ZType; i: IType; success, failure: MemoryOrder) = BEGIN IF NOT LegalStore [z, m] THEN PutErr (self, "illegal store conversion"); END; IF NOT LegalLoad [m, z] THEN PutErr (self, "illegal load conversion"); END; CASE failure OF | MemoryOrder.Release, MemoryOrder.AcquireRelease => PutErr (self, "illegal load memory order"); ELSE (* ok *) END; IF failure > success THEN PutErr (self, "failure stronger than success"); END; self.s_pop (T_to_ST [z], ST.Addr, ST.Addr); self.s_push (i); self.child.compare_exchange (m, z, i, success, failure); END compare_exchange; PROCEDUREfence (self: U; order: MemoryOrder) = BEGIN self.child.fence (order); END fence; PROCEDUREfetch_and_op (self: U; op: AtomicOp; m: MType; z: ZType; order: MemoryOrder) = BEGIN IF NOT LegalStore [z, m] THEN PutErr (self, "illegal store conversion"); END; IF NOT LegalLoad [m, z] THEN PutErr (self, "illegal load conversion"); END; self.s_pop (T_to_ST[z], ST.Addr); self.s_push (z); self.child.fetch_and_op (op, m, z, order); END fetch_and_op; BEGIN END M3CG_Check.