Module: java-vm-code-generation Author: Mark Tillotson Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND format-out ("initing java-emit-code.dylan\n") ; // should be able to get rid of this define sealed class () sealed slot pc = #f ; end; define constant $initial-jbb-vecsize$ = 15 ; define variable *break-on-non-verify* :: = #f ; define class () sealed slot meth :: , init-keyword: meth: ; sealed slot bytecodes :: = make (, size: $initial-jbb-vecsize$) ; sealed slot the-label :: = make () ; sealed slot icount :: = 0 ; sealed slot tcount :: = $initial-jbb-vecsize$ ; sealed slot initial-stack-depth = #f ; sealed slot stack-depth :: = 0 ; sealed slot initial-stack-model = #() ; sealed slot stack-model = #() ; sealed slot initial-local-var-types = #f ; sealed slot local-var-types = #f ; sealed slot constants ; // just cache the value in the for easier debugging end; define variable *inside-bb-generation* :: = #f ; define function make-jbb (jmeth :: ) => (jbb :: ) // if (*inside-bb-generation*) // error ("make-jbb called inside bb generation!") // end; *inside-bb-generation* := #t ; let opc = jmeth.pc ; let jbb = make (, meth: jmeth, pc: opc) ; jbb.the-label.pc := opc ; jmeth.basic-blocks := add! (jmeth.basic-blocks, jbb) ; jbb.constants := jmeth.java-class.concrete-implementation.constants ; jbb end; define sealed abstract class () end; define class () sealed slot imm-value :: , required-init-keyword: imm-value: ; end; define class () sealed slot opcode :: , required-init-keyword: opcode: ; end; define constant $dummy-java-frag$ :: = make (, opcode: j-nop) ; define sealed generic frag-size (frag :: ) => (size :: ) ; //define method frag-size-setter (size :: , frag :: ) => (size :: ) size end; define method frag-size (frag :: ) => (size :: ) 1 end ; define method frag-size (frag :: ) => (size :: ) 1 end ; define method print-object (frag :: , stream :: ) => () format (stream, "{", frag.opcode.opname) end; define method print-object (frag :: , stream :: ) => () format (stream, "{", frag.imm-value, frag.frag-size) end; define sealed generic output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) ; // hack for emulator?? define function bytify (i :: ) => (res :: ) i := logand (i, #xff) ; // if (i >= #x80) // i := i - #x100 // end; i end; define method output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) vec [peecee] := bytify (frag.imm-value) ; peecee + 1 end; define method output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) vec [peecee] := bytify (frag.opcode.opcode) ; peecee + 1 end; define class () sealed slot op, init-keyword: op: ; end; define method print-object (frag :: , stream :: ) => () format (stream, "{", frag.opcode.opname, frag.op) end; define method frag-size (frag :: ) => (size :: ) 2 end ; //define method initialize (frag :: , #key) => () // next-method () ; //// frag.frag-size := 2 //end; define method output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) vec [peecee] := bytify (frag.opcode.opcode) ; vec [peecee + 1] := bytify (frag.op) ; peecee + 2 end; define class () sealed slot constants, required-init-keyword: constants: ; // purely for printing nicely end; define method print-object (frag :: , stream :: ) => () let index = frag.op ; format (stream, "{}") end; define method frag-size (frag :: ) => (size :: ) 3 end ; //define method initialize (frag :: , #key) => () // next-method () ; //// frag.frag-size := 3 //end; define method output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) vec [peecee] := bytify (frag.opcode.opcode) ; vec [peecee + 1] := bytify (ash (frag.op, -8)) ; vec [peecee + 2] := bytify (frag.op) ; peecee + 3 end; // this only used for invokeinterface define class () sealed slot nargs :: , required-init-keyword: nargs: ; end; define method print-object (frag :: , stream :: ) => () let index = frag.op ; format (stream, "{}") end; define method frag-size (frag :: ) => (size :: ) 5 end ; define method output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) vec [peecee] := bytify (frag.opcode.opcode) ; vec [peecee + 1] := bytify (ash (frag.op, -8)) ; vec [peecee + 2] := bytify (frag.op) ; vec [peecee + 3] := bytify (frag.nargs) ; vec [peecee + 4] := 0 ; peecee + 5 end; define class () sealed slot meth, init-keyword: meth: ; sealed slot dest, init-keyword: dest: ; end; define method print-object (frag :: , stream :: ) => () format (stream, "{ %s>", frag.opcode.opname, frag.meth, frag.dest) end; define method frag-size (frag :: ) => (size :: ) 3 end ; //define method initialize (frag :: , #key) => () // next-method () ; //// frag.frag-size := 3 //end; define constant $relative-tag$ :: = 1000000 ; define function branch-relative (branch-offset :: ) => (off :: ) branch-offset + $relative-tag$ end; define open generic resolve-branch-dest (thing, meth :: , peecee :: ) => (offset :: ) ; define method resolve-branch-dest (thing :: , meth :: , peecee :: ) => (offset :: ) error ("bad dest for fragment %s(%s)\n", thing, thing.object-class) end; define method resolve-branch-dest (thing :: , meth :: , peecee :: ) => (offset :: ) let offset :: = thing() ; offset end; define method resolve-branch-dest (thing :: , meth :: , peecee :: ) => (offset :: ) if (thing >= $relative-tag$) // hack for relative (thing - $relative-tag$) - peecee else thing end end; define method output-frag (peecee :: , vec :: , frag :: ) => (npc :: ) vec [peecee] := frag.opcode.opcode.bytify ; let offs :: = resolve-branch-dest (frag.dest, frag.meth, peecee) ; vec [peecee + 1] := bytify (ash (offs, -8)) ; vec [peecee + 2] := offs.bytify ; peecee + 3 end; // actually stash the frag into a vector, maintain the true pc count. define function add-bytecode (jbb :: , byte :: ) => () let ic :: = jbb.icount ; let tc :: = jbb.tcount ; let bcodes :: = jbb.bytecodes ; if (ic == tc) let ntc = 2 * tc ; let new :: = make (, size: ntc) ; jbb.tcount := ntc ; for (n :: from 0 below ic) new[n] := bcodes[n] end; jbb.bytecodes := bcodes := new end; bcodes [ic] := byte ; jbb.icount := ic + 1 ; jbb.pc := jbb.pc + byte.frag-size end; define variable *debug-jvm-instrs* = 4 ; // maintain the model of stack depth within a BB - collect the max depth as // well as tracking the current depth. define function maintain-stack-depth (jbb :: , pushes :: , op :: ) => () if (*debug-jvm-instrs*) format-out ("... instruction %s changes depth from %d to %d\n", op, jbb.stack-depth, jbb.stack-depth + pushes) end; unless (zero? (pushes)) let new-depth :: = jbb.stack-depth + pushes ; if (new-depth > jbb.max-stack) jbb.max-stack := new-depth elseif (negative? (new-depth)) if (*debug-jvm-instrs*) format-out ("############## negative JVM stack depth!! %s\n", op) ; java-marker-op (jbb) ; else error ("negative JVM stack depth in Java backend") end; new-depth := 0 end; jbb.stack-depth := new-depth end end; define sealed generic model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) ; define sealed generic model-a-pop (jbb :: , oper :: , pushee :: ) => (words :: ) ; define function model-pop-discards (jbb :: , count :: ) => (words :: ) let original-depth = jbb.stack-depth ; let depth = original-depth ; let model = jbb.stack-model ; for (n :: from 0 below count) unless (instance? (model, )) error ("empty stack on pop-discarding") end; let tipe = model.head ; model := model.tail ; depth := depth - tipe.java-type-words end; jbb.stack-model := model ; jbb.stack-depth := depth ; depth - original-depth end; define function model-push-type (jbb :: , tipe :: ) => (words :: ) jbb.stack-model := pair (tipe, jbb.stack-model) ; let count = tipe.java-type-words ; jbb.stack-depth := jbb.stack-depth + count ; count end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) let pool :: = jbb.meth.java-class.concrete-implementation.constants ; if (instance? (oper, )) let index :: = oper.op ; let constant = pool [index] ; let tipe = constant.constant-javatype ; model-push-type (jbb, tipe) else error ("not an op fragment in pushed constant?!") end end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) let pool :: = jbb.meth.java-class.concrete-implementation.constants ; if (instance? (oper, )) let index :: = oper.op ; let constant = pool [index] ; let tipe = constant.constant-javatype ; if (*debug-jvm-instrs* == #t) format-out ("pushing a field, type %s\n", tipe) end; model-push-type (jbb, tipe) else error ("not an op fragment in pushed field?!") end end; define constant $prim-array-code-lookup$ = vector (#f, #f, #f, #f, $java-bool-type$, $java-char-type$, $java-float-type$, $java-double-type$, $java-byte-type$, $java-short-type$, $java-int-type$, $java-long-type$) ; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) let tipe = #f ; let index :: = oper.op ; if (pushee.prim?) tipe := element ($prim-array-code-lookup$, index, default: #f) ; unless (tipe) error ("Huh? bad primitive array typecode") end; else let pool :: = jbb.meth.java-class.concrete-implementation.constants ; tipe := pool[index].constant-javatype ; end; model-push-type (jbb, array-type (tipe)) end; define function model-push-a-local (jbb :: , index :: ) => (words :: ) let var-types = jbb.local-var-types ; let tipe = var-types & var-types [index] ; unless (tipe) if (*debug-jvm-instrs*) format-out ("pushing an Uninitialized local var %d\n", index) ; java-marker-op (jbb) ; tipe := $java/lang/Object$ else error ("pushing an Uninitialized local var %d", index) end end; if (*debug-jvm-instrs* == #t) format-out ("@@@ model-push-a-local, index=%d, type=%s\n", index, tipe) end; model-push-type (jbb, tipe) end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) let tipe = pushee.type-constraint ; unless (tipe) error ("model-push has bad type-constraint") end; model-push-type (jbb, tipe) end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) model-push-a-local (jbb, pushee.local-var-num) end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) model-push-a-local (jbb, oper.op) end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) let tipe = pushee.type-variable ; if (tipe == #f) if (*debug-jvm-instrs*) format-out ("unassigned type metavar\n") ; java-marker-op (jbb) ; tipe := $java/lang/Object$ else error ("unassigned type metavar") end end; if (instance? (pushee, )) if (instance? (tipe, )) jbb.stack-model := pair (tipe.tail, pair (tipe.head, jbb.stack-model)) ; else jbb.stack-model := pair (tipe, jbb.stack-model) ; end; jbb.stack-depth := jbb.stack-depth + 2 ; 2 else jbb.stack-model := pair (tipe, jbb.stack-model) ; jbb.stack-depth := jbb.stack-depth + 1 ; 1 end end; define function model-pop-a-type (jbb :: , tipe :: ) => (words :: ) let list = jbb.stack-model ; let top = #f ; let rest = #f ; if (empty? (list)) if (*debug-jvm-instrs*) format-out ("trying to pop a model from empty stack model! faking it\n") ; java-marker-op (jbb) ; top := $java/lang/Object$ ; rest := list else error ("trying to pop a model from empty stack model") end else top := list.head ; rest := list.tail ; end; let count = tipe.java-type-words ; unless (assignment-compatible? (top, tipe)) if (*debug-jvm-instrs*) format-out ("@@@@@@ not ass comp, %s, %s, ignoring problem\n", top, tipe) ; java-marker-op (jbb) ; if (*break-on-non-verify*) my-break (jbb) end else error ("not java assignment compatible") end end; jbb.stack-model := rest ; jbb.stack-depth := jbb.stack-depth - count ; count end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) model-pop-a-type (jbb, poppee.type-constraint) end; define constant $max-local-number$ = #x100 ; // don't support "wide" yet (well, nearly) define function model-set-a-local (jbb :: , index :: , new-tipe :: ) => (final-tipe :: ) unless (new-tipe) error ("whhops in model-set-a-local") end; let var-types = jbb.local-var-types ; unless (var-types) var-types := make (, size: $max-local-number$, fill: #f) ; jbb.local-var-types := var-types end; let old-tipe = var-types [index] ; if (*debug-jvm-instrs* == #t) format-out ("@@@ model-set-local %d, was %s, new %s\n", index, old-tipe, new-tipe) end; if (old-tipe) if (assignment-compatible? (new-tipe, old-tipe)) old-tipe else /* if (*debug-jvm-instrs*) java-marker-op (jbb) ; format-out ("@@@@@@ not ass comp, %s, %s\n", new-tipe, old-tipe) else error ("badly typed local assign") end; */ // HACK to allow arbitrary retypings for now var-types [index] := new-tipe end else var-types [index] := new-tipe ; end end; define function model-pop-a-local (jbb :: , index :: ) => (words :: ) let list = jbb.stack-model ; let top-type = list.head ; format-out ("top type is %s\n", top-type) ; jbb.stack-model := list.tail ; let var-new-type = model-set-a-local (jbb, index, top-type) ; let count = var-new-type.java-type-words ; jbb.stack-depth := jbb.stack-depth - count ; count end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) let pool :: = jbb.meth.java-class.concrete-implementation.constants ; if (instance? (oper, )) let index :: = oper.op ; let constant = pool [index] ; if (instance? (constant, )) let tipe = constant.constant-javatype ; if (*debug-jvm-instrs* == #t) format-out ("popping a field, type %s\n", tipe) end; model-pop-a-type (jbb, tipe) else error ("not a java slot in popped field?!") end else error ("not an op fragment in popped field?!") end end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) let pool :: = jbb.meth.java-class.concrete-implementation.constants ; if (instance? (oper, )) let index :: = oper.op ; let constant = pool [index] ; if (instance? (constant, )) let tipe = constant.java-class.java-class ; // want the class of the field! if (*debug-jvm-instrs* == #t) format-out ("popping a field instance, type %s\n", tipe) end; model-pop-a-type (jbb, tipe) else error ("not a java slot in popped field instance?!") end else error ("not an op fragment in popped field instance?!") end end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) error ("model-a-pop with an ABSTRASCT !") end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) model-pop-a-local (jbb, poppee.local-var-num) end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) model-pop-a-local (jbb, oper.op) end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) let list = jbb.stack-model ; if (empty? (list)) if (*debug-jvm-instrs*) format-out ("@@@@@ popping from empty stack model\n") end; if (*break-on-non-verify*) my-break (jbb) end end; let top = list.head ; let rest = list.tail ; let count = top.java-type-words ; if (count < 1 | count > 2) error ("wrong word size for a popped JVM stack item %d", count) end; if (count = 2) if (instance? (poppee, )) jbb.stack-model := rest ; poppee.type-variable := top else if (*debug-jvm-instrs*) java-marker-op (jbb) ; format-out ("@@@@@ popping double word value into singleword metavar\n") end; if (*break-on-non-verify*) my-break (jbb) end end elseif (instance? (poppee, )) let top2 = rest.head ; rest := rest.tail ; if (top2.java-type-words ~= 1) if (*debug-jvm-instrs*) format-out ("@@@@@ popping misaligned doubleword into doubleword metavar\n") ; java-marker-op (jbb) ; if (*break-on-non-verify*) my-break (jbb) end else error ("popping misaligned doubleword into doubleword metavar") end end; jbb.stack-model := rest ; poppee.type-variable := pair (top, top2) ; count := 2 else jbb.stack-model := rest ; poppee.type-variable := top end; jbb.stack-depth := jbb.stack-depth - count ; count end; define method model-a-pop (jbb :: , oper :: , poppee :: ) => (words :: ) let pool :: = jbb.meth.java-class.concrete-implementation.constants ; if (instance? (oper, )) let index :: = oper.op ; let constant = pool [index] ; if (instance? (constant, )) let tipe = constant.java-class ; // want the class of the field! if (*debug-jvm-instrs* == #t) format-out ("@@@ popping a checkcast metavar, type %s\n", tipe) end; let model = jbb.stack-model ; if (instance? (model, )) // was wrong: poppee.type-variable := tipe ; jbb.stack-model := pair (tipe, model.tail) ; next-method () // this does the actual work else error ("empty stack in checkcast") end else error ("not a class constant in checkcast") end else error ("not a proper checkcast instruction type") end end; define method model-a-push (jbb :: , oper :: , pushee :: ) => (words :: ) //format-out ("model pushing a return address\n") ; model-push-type (jbb, $java-return-address$) end; define method model-a-pop (jbb :: , oper :: , pushee :: ) => (words :: ) model-pop-a-type (jbb, $java-return-address$) end; define function maintain-stack-types (jbb :: , frag :: , things-to-pop :: , things-to-push :: ) => () let depth :: = jbb.stack-depth ; //format-out ("#!# depth before %d ", depth) ; for (poppee :: in things-to-pop) depth := depth - model-a-pop (jbb, frag, poppee) ; if (*debug-jvm-instrs* == #t) format-out ("@@@ ") ; for (i :: from 0 below depth + 1) format-out (". ") end; format-out ("%s popping %s\n", frag, poppee) end; if (negative? (depth)) //format-out ("!depth after! %d\n", depth) ; error ("JVM stack underflow internal error") end end; for (pushee :: in things-to-push) if (*debug-jvm-instrs* == #t) format-out ("@@@ ") ; for (i :: from 0 below depth + 1) format-out (". ") end; format-out ("%s pushing %s\n", frag, pushee) end; depth := depth + model-a-push (jbb, frag, pushee) end; //format-out ("depth after %d\n", depth) ; if (*debug-jvm-instrs* == #t) format-out ("@@@\n") end; if (depth > jbb.max-stack) if (depth > #xFFFF) error ("JVM stack overflow") end; jbb.max-stack := depth end; jbb.stack-depth := depth end; define function merge-stack-types (types1 :: , types2 :: ) if (types1 == types2) // optimize shared common stack types1 else if (instance? (types1, ) & instance? (types2, )) let rest = merge-stack-types (types1.tail, types2.tail) ; let this = java-type-merge (types1.head, types2.head) ; // try to keep sharing structure if (this == types1.head & rest == types1.tail) types1 elseif (this == types2.head & rest == types2.tail) types2 else pair (this, rest) end elseif (empty? (types1) & empty? (types2)) #() else if (*debug-jvm-instrs*) format-out ("@@@@@ failed to merge stack types\n") ; // can't do this!! java-marker-op (jbb) ; if (*break-on-non-verify*) my-break (types1) end else error ("failed to merge stack types") end end end end; define function merge-local-var-types (vars1 :: , vars2 :: ) => (result :: ) let new = make (, size: $max-local-number$, fill: #f) ; for (i :: from 0 below $max-local-number$) let type1 = if (vars1) vars1[i] end ; let type2 = if (vars2) vars2[i] end ; new [i] := type1 & type2 & java-type-merge (type1, type2) ; end; new end; define function merge-bbs-types (src-bb :: , dest-bb :: ) if (dest-bb.initial-local-var-types) if (*debug-jvm-instrs*) format-out ("@@@ augmenting stack models from bb to bb\n") end; if (dest-bb.initial-stack-depth ~= src-bb.stack-depth) if (*debug-jvm-instrs*) format-out ("#################### mismatched JVM stack depths\n") else error ("mismatched JVM stack depths in Java backend") end end; dest-bb.initial-stack-model := merge-stack-types (src-bb.stack-model, dest-bb.initial-stack-model) ; dest-bb.initial-local-var-types := merge-local-var-types (src-bb.local-var-types, dest-bb.initial-local-var-types) else // first seen... if (*debug-jvm-instrs*) format-out ("@@@ copying stack models from bb to bb\n") end; dest-bb.stack-depth := dest-bb.initial-stack-depth := src-bb.stack-depth ; dest-bb.stack-model := dest-bb.initial-stack-model := src-bb.stack-model ; if (src-bb.local-var-types) dest-bb.initial-local-var-types := copy-sequence (src-bb.local-var-types) ; dest-bb.local-var-types := copy-sequence (src-bb.local-var-types) ; end end end; // the max-locals is not normally maintained on a per-instruction basis, but // some low-level code generation (not using number-local-var) might // set max-locals for a , so we propagate this out as // well as the max stack depth. define function finish-with-jbb (jbb :: , jmeth :: ) => () // unless (*inside-bb-generation*) // error ("finish-with-jbb called outside bb generation") // end; if (*debug-jvm-instrs* == #t) format-out ("@@@ propagating pc %d to method\n", jbb.pc) end; jmeth.pc := jbb.pc ; jmeth.max-stack := max (jmeth.max-stack, jbb.max-stack) ; jmeth.max-locals := max (jmeth.max-locals, jbb.max-locals) ; *inside-bb-generation* := #f ; if (*debug-jvm-instrs* == #t) format-out ("end bb\n") end; if (jbb.stack-depth > 0) // some bbs will have something on the stack // but I'll catch the obviously wrong ones this way format-out ("############# BB has non-empty stack at end!\n") ; for (ppm in jbb.stack-model, n from 0) format-out ("@@@%s: %s\n", n, ppm) end; if (*break-on-non-verify*) my-break (jbb) end end end; define variable *check-stack-types* :: = #t ; define function ensure-stack-model (from-jbb :: , to-jbb :: ) => () if (*debug-jvm-instrs*) format-out ("@@@ ensure-stack-model\n") end; if (*check-stack-types*) merge-bbs-types (from-jbb, to-jbb) else let from-depth = from-jbb.stack-depth ; let to-depth = to-jbb.initial-stack-depth ; if (to-depth) if (to-depth ~= from-depth) if (*debug-jvm-instrs*) java-marker-op (from-jbb) ; format-out ("#################### mismatched JVM stack depths\n") else error ("mismatched JVM stack depths in Java backend") end end else to-jbb.initial-stack-depth := from-depth ; to-jbb.stack-depth := from-depth ; end end; end; // this reduces consing define variable *java-simple-op-cache* = make (, size: #x100) ; // this is for all the argument-less 1-byte opcodes, reduce pointless allocation // could just use an integer, of course! define function java-simple-op (jbb :: , oper :: ) => () let cache = *java-simple-op-cache* ; let opcde = oper.opcode ; let cached-frag = cache [opcde] | (cache [opcde] := make (, opcode: oper)) ; add-bytecode (jbb, cached-frag) ; if (*check-stack-types*) maintain-stack-types (jbb, cached-frag, oper.pop-list, oper.push-list) else maintain-stack-depth (jbb, oper.push-count, oper) end end; define function java-marker-op (jbb :: ) => () /* let cache = *java-simple-op-cache* ; let opcde = j-nop.opcode ; let cached-frag = cache [opcde] | (cache [opcde] := make (, opcode: j-nop)) ; add-bytecode (jbb, cached-frag) ; add-bytecode (jbb, cached-frag) ; add-bytecode (jbb, cached-frag) */ end; define function java-op1-op (jbb :: , oper :: , op :: ) => () let frag = make (, opcode: oper, op: op) ; add-bytecode (jbb, frag) ; if (*check-stack-types*) maintain-stack-types (jbb, frag, oper.pop-list, oper.push-list) else maintain-stack-depth (jbb, oper.push-count, oper) end end; // this turns a spec into a - isn't this really redundant? define sealed generic make-java-constant (thing) => (const :: ) ; define method make-java-constant (thing :: ) => (const :: ) make (, java-class: thing) end; // weird inconsistency in JVM, classes can be named by just name, array-classes // require a signature proper define method make-java-constant (thing :: ) => (const :: ) make (, java-class: thing) end; define method make-java-constant (thing :: ) => (const :: ) make (, java-class: make (, java-class: thing.java-class), nat: make (, name: thing.slot-name, type: thing.slot-type)) end; define method make-java-constant (thing :: ) => (const :: ) make (, java-class: make (, java-class: thing.java-class), nat: make (, name: thing.slot-name, type: thing.slot-type)) end; define function java-op2-op (jbb :: , oper :: , op :: ) let frag = make (, opcode: oper, op: op, constants: jbb.constants) ; add-bytecode (jbb, frag) ; if (*check-stack-types*) maintain-stack-types (jbb, frag, oper.pop-list, oper.push-list) else maintain-stack-depth (jbb, oper.push-count, oper) end end; define function java-op2 (jbb :: , oper :: , thing) java-op2-op (jbb, oper, pool-index (make-java-constant (thing), jbb.meth.java-class.concrete-implementation)) end; define function java-call (jbb :: , meth-spec :: ) let oper :: = meth-spec.invoke-op ; let frag = make (, opcode: oper, op: pool-index (make-java-constant (meth-spec), jbb.meth.java-class.concrete-implementation), constants: jbb.constants) ; add-bytecode (jbb, frag) ; if (*check-stack-types*) maintain-stack-types (jbb, frag, meth-spec.pop-list, meth-spec.push-list) else maintain-stack-depth (jbb, meth-spec.total-pushes, meth-spec.invoke-op) end end; define function java-if-call (jbb :: , meth-spec :: ) let oper :: = meth-spec.invoke-op ; let frag = make (, opcode: oper, op: pool-index (make-java-constant (meth-spec), jbb.meth.java-class.concrete-implementation), nargs: meth-spec.slot-type.java-function-arg-types.size, constants: jbb.constants) ; add-bytecode (jbb, frag) ; if (*check-stack-types*) maintain-stack-types (jbb, frag, meth-spec.pop-list, meth-spec.push-list) else maintain-stack-depth (jbb, meth-spec.total-pushes, meth-spec.invoke-op) end end; // caller has to deal with instance for getfield define function java-read (jbb :: , slot-spec :: ) // should test for 2-words too let oper = if (slot-spec.static?) j-getstatic else j-getfield end; java-op2 (jbb, oper, slot-spec) end; // caller has to deal with instance for putfield define function java-write (jbb :: , slot-spec :: ) // should test for 2-words too let oper = if (slot-spec.static?) j-putstatic else j-putfield end; java-op2 (jbb, oper, slot-spec) end; define function java-imm (jbb :: , imm :: ) let frag = make (, imm-value: imm, meth: jbb.meth) ; add-bytecode (jbb, frag) ; end; define function java-branch-op (jbb :: , oper :: , dest) let frag = make (, opcode: oper, dest: dest, meth: jbb.meth) ; add-bytecode (jbb, frag) ; if (*check-stack-types*) maintain-stack-types (jbb, frag, oper.pop-list, oper.push-list) else maintain-stack-depth (jbb, oper.push-count, oper) end; // now check/set dest block to have right stack depth end; define function output-bytecodes (outvec :: , jbb :: , peecee :: ) if (peecee ~= jbb.the-label.pc) format-out ("warn: bytecode PCs don't match up %s %s\n", peecee, jbb.the-label.pc) end; let bcodes :: = jbb.bytecodes ; let icnt :: = jbb.icount ; for (n :: from 0 below icnt) peecee := output-frag (peecee, outvec, bcodes[n]) end; peecee end; // some code gen utilities define constant $j-local-var-pushes = vector (vector (j-iload-0, j-iload-1, j-iload-2, j-iload-3, j-iload), vector (j-lload-0, j-lload-1, j-lload-2, j-lload-3, j-lload), vector (j-fload-0, j-fload-1, j-fload-2, j-fload-3, j-fload), vector (j-dload-0, j-dload-1, j-dload-2, j-dload-3, j-dload), vector (j-aload-0, j-aload-1, j-aload-2, j-aload-3, j-aload)) ; define constant $j-local-var-pops = vector (vector (j-istore-0, j-istore-1, j-istore-2, j-istore-3, j-istore), vector (j-lstore-0, j-lstore-1, j-lstore-2, j-lstore-3, j-lstore), vector (j-fstore-0, j-fstore-1, j-fstore-2, j-fstore-3, j-fstore), vector (j-dstore-0, j-dstore-1, j-dstore-2, j-dstore-3, j-dstore), vector (j-astore-0, j-astore-1, j-astore-2, j-astore-3, j-astore)) ; define function emit-local-var-op (jbb :: , offset :: , jtype :: , opv :: ) let ops :: = opv [jtype] ; if (offset < 4) java-simple-op (jbb, ops [offset]) else if (offset >= #x100) java-simple-op (jbb, j-wide) ; java-op1-op (jbb, ops [4], ash (offset, -8)) ; java-imm (jbb, logand (offset, #xff)) else java-op1-op (jbb, ops [4], offset) end end end; define function emit-ret (jbb :: , offset :: ) if (offset >= #x100) java-simple-op (jbb, j-wide) ; java-op1-op (jbb, j-ret, ash (offset, -8)) ; java-imm (jbb, logand (offset, #xff)) else java-op1-op (jbb, j-ret, offset) end end; define function emit-java-ldc (jbb :: , ind :: ) => () if (ind < #x100) java-op1-op (jbb, j-ldc1, ind) else java-op2-op (jbb, j-ldc2, ind) end end; define function emit-java-int (jbb :: , int :: ) => () if (int <= 5 & int >= -1) java-simple-op (jbb, j-iconsts [int + 1]) else if (int < #x80 & int >= - #x80) java-op1-op (jbb, j-bipush, int) else if (int < #x8000 & int >= - #x8000) java-op2-op (jbb, j-sipush, int) ; else let rep = make (, value: int) ; let ind = pool-index (rep, jbb.meth.java-class.concrete-implementation) ; emit-java-ldc (jbb, ind) end end end end; define function emit-java-string (jbb :: , str :: ) => () let con = make (, utf: make (, string: str.uniq)); emit-java-constant-load (jbb, con) end; define open generic emit-java-constant-load (jbb :: , const) => () ; // handle constants define method emit-java-constant-load (jbb :: , const :: ) => () emit-java-ldc (jbb, pool-index (const, jbb.meth.java-class.concrete-implementation)) end; // handle some common types define method emit-java-constant-load (jbb :: , flt :: ) => () emit-java-ldc (jbb, pool-index (make (, value: flt), jbb.meth.java-class.concrete-implementation)) end; define method emit-java-constant-load (jbb :: , flt :: ) => () emit-java-ldc (jbb, pool-index (make (, value: flt), jbb.meth.java-class.concrete-implementation)) end; define function emit-push-local (jbb :: , offset :: , jtype :: ) emit-local-var-op (jbb, offset, jtype, $j-local-var-pushes) end; define function emit-push-this (jbb :: ) emit-local-var-op (jbb, 0, j-ref-code, $j-local-var-pushes) // j-code-for (jbb.meth.java-class) end; define function emit-pop-local (jbb :: , offset :: , jtype :: ) emit-local-var-op (jbb, offset, jtype, $j-local-var-pops) end; define function emit-pop (jbb :: ) => () java-simple-op (jbb, j-pop) end; define function emit-dup (jbb :: ) => () java-simple-op (jbb, j-dup) end; define function emit-swap (jbb :: ) => () java-simple-op (jbb, j-swap) end; define constant j-returns = vector (j-ireturn, j-lreturn, j-freturn, j-dreturn, j-areturn, j-return) ; define function emit-return (jbb :: , jtype :: ) => () java-simple-op (jbb, j-returns[jtype]) end; // handle a slot define method emit-java-constant-load (jbb :: , const :: ) => () if (const.static?) java-read (jbb, const) else emit-push-this (jbb) ; java-read (jbb, const) end end; format-out ("inited java-emit-code.dylan\n") ; // eof