/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * CodeGenerator.java * Created: Dec 3, 2002 at 7:24:06 PM * By: Raymond Cypher */ package org.openquark.cal.internal.machine.g; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.SortedMap; import java.util.logging.Formatter; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.StreamHandler; import org.openquark.cal.compiler.CompilerMessage; import org.openquark.cal.compiler.CompilerMessageLogger; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.compiler.Expression; import org.openquark.cal.compiler.FieldName; import org.openquark.cal.compiler.MessageKind; import org.openquark.cal.compiler.MessageLogger; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.compiler.UnableToResolveForeignEntityException; import org.openquark.cal.compiler.Expression.Switch.SwitchAlt; import org.openquark.cal.internal.machine.BasicOpTuple; import org.openquark.cal.internal.machine.CodeGenerationException; import org.openquark.cal.internal.machine.CondTuple; import org.openquark.cal.internal.machine.ConstructorOpTuple; import org.openquark.cal.internal.machine.g.functions.NAppendRecordPrimitive; import org.openquark.cal.internal.machine.g.functions.NArbitraryRecordPrimitive; import org.openquark.cal.internal.machine.g.functions.NBuildList; import org.openquark.cal.internal.machine.g.functions.NBuildRecord; import org.openquark.cal.internal.machine.g.functions.NCoArbitraryRecordPrimitive; import org.openquark.cal.internal.machine.g.functions.NCompareRecord; import org.openquark.cal.internal.machine.g.functions.NDeepSeq; import org.openquark.cal.internal.machine.g.functions.NEqualsRecord; import org.openquark.cal.internal.machine.g.functions.NError; import org.openquark.cal.internal.machine.g.functions.NInsertOrdinalRecordFieldPrimitive; import org.openquark.cal.internal.machine.g.functions.NInsertTextualRecordFieldPrimitive; import org.openquark.cal.internal.machine.g.functions.NNotEqualsRecord; import org.openquark.cal.internal.machine.g.functions.NOrdinalValue; import org.openquark.cal.internal.machine.g.functions.NPrimCatch; import org.openquark.cal.internal.machine.g.functions.NPrimThrow; import org.openquark.cal.internal.machine.g.functions.NRecordFieldTypePrimitive; import org.openquark.cal.internal.machine.g.functions.NRecordFieldValuePrimitive; import org.openquark.cal.internal.machine.g.functions.NRecordFromJListPrimitive; import org.openquark.cal.internal.machine.g.functions.NRecordFromJMapPrimitive; import org.openquark.cal.internal.machine.g.functions.NRecordToJListPrimitive; import org.openquark.cal.internal.machine.g.functions.NRecordToJRecordValuePrimitive; import org.openquark.cal.internal.machine.g.functions.NRecordTypeDictionary; import org.openquark.cal.internal.machine.g.functions.NSeq; import org.openquark.cal.internal.machine.g.functions.NShowRecord; import org.openquark.cal.internal.machine.g.functions.NStrictRecordPrimitive; import org.openquark.cal.internal.machine.primitiveops.PrimOps; import org.openquark.cal.machine.MachineFunction; import org.openquark.cal.machine.Module; import org.openquark.cal.machine.StatusListener; import org.openquark.cal.module.Cal.Core.CAL_Prelude; import org.openquark.cal.runtime.ErrorInfo; import org.openquark.cal.runtime.MachineConfiguration; /** * Convert Expression instances into g-machine instruction sequences. * * <p> * Created: Dec 3, 2002 at 7:24:05 PM * @author Raymond Cypher */ class CodeGenerator extends org.openquark.cal.machine.CodeGenerator { /** The namespace for log messages from the G machine. */ public static final String MACHINE_LOGGER_NAMESPACE = "org.openquark.cal.internal.runtime.g"; /** An instance of a Logger for G machine messages. */ static final Logger MACHINE_LOGGER = Logger.getLogger(MACHINE_LOGGER_NAMESPACE); /** A map of QualifiedName -> NPrimitiveFunc for primitive functions. */ private static final Map<QualifiedName, NPrimitiveFunc> primitiveFuncMap = new HashMap<QualifiedName, NPrimitiveFunc> (); /** Flag indicating that function tracing and breakpoints are enabled and */ private static final boolean GENERATE_DEBUG_CODE = System.getProperty(MachineConfiguration.MACHINE_DEBUG_CAPABLE_PROP) != null; static { // Initialise the primitive function map. primitiveFuncMap.put (NDeepSeq.name, NDeepSeq.instance); primitiveFuncMap.put (NOrdinalValue.name, NOrdinalValue.instance); primitiveFuncMap.put (NShowRecord.name, NShowRecord.instance); primitiveFuncMap.put (NRecordFieldValuePrimitive.name, NRecordFieldValuePrimitive.instance); primitiveFuncMap.put (NRecordFieldTypePrimitive.name, NRecordFieldTypePrimitive.instance); primitiveFuncMap.put (NInsertTextualRecordFieldPrimitive.name, NInsertTextualRecordFieldPrimitive.instance); primitiveFuncMap.put (NInsertOrdinalRecordFieldPrimitive.name, NInsertOrdinalRecordFieldPrimitive.instance); primitiveFuncMap.put (NAppendRecordPrimitive.name, NAppendRecordPrimitive.instance); primitiveFuncMap.put (NRecordTypeDictionary.name, NRecordTypeDictionary.instance); primitiveFuncMap.put (NRecordToJListPrimitive.name, NRecordToJListPrimitive.instance); primitiveFuncMap.put (NRecordFromJListPrimitive.name, NRecordFromJListPrimitive.instance); primitiveFuncMap.put (NRecordFromJMapPrimitive.name, NRecordFromJMapPrimitive.instance); primitiveFuncMap.put (NRecordToJRecordValuePrimitive.name, NRecordToJRecordValuePrimitive.instance); primitiveFuncMap.put (NStrictRecordPrimitive.name, NStrictRecordPrimitive.instance); primitiveFuncMap.put (NCompareRecord.name, NCompareRecord.instance); primitiveFuncMap.put (NNotEqualsRecord.name, NNotEqualsRecord.instance); primitiveFuncMap.put (NEqualsRecord.name, NEqualsRecord.instance); primitiveFuncMap.put (NArbitraryRecordPrimitive.name, NArbitraryRecordPrimitive.instance); primitiveFuncMap.put (NCoArbitraryRecordPrimitive.name, NCoArbitraryRecordPrimitive.instance); primitiveFuncMap.put (NError.name, NError.instance); primitiveFuncMap.put (NSeq.name, NSeq.instance); primitiveFuncMap.put (NPrimCatch.name, NPrimCatch.instance); primitiveFuncMap.put (NPrimThrow.name, NPrimThrow.instance); primitiveFuncMap.put (NBuildList.name, NBuildList.instance); primitiveFuncMap.put (NBuildRecord.name, NBuildRecord.instance); MACHINE_LOGGER.setLevel(Level.FINEST); MACHINE_LOGGER.setLevel(Level.FINEST); MACHINE_LOGGER.setUseParentHandlers(false); StreamHandler consoleHandler = new StreamHandler(System.out, new ConsoleFormatter()) { /** Override this to always flush the stream. */ @Override public void publish(LogRecord record) { super.publish(record); flush(); } /** Override to just flush the stream, we don't want to close System.out. */ @Override public void close() { flush(); } }; consoleHandler.setLevel(Level.ALL); MACHINE_LOGGER.addHandler(consoleHandler); } protected GMachineFunction currentMachineFunction; //protected CompilerMessageLogger logger; /** Show code generation diagnostics. */ public static boolean CODEGEN_DIAG = false; private KeyholeOptimizer ko = new KeyholeOptimizer (); private Module currentModule; /** * Construct CodeGenerator from compiler. * @param isForAdjunct */ CodeGenerator(boolean isForAdjunct) { super (isForAdjunct); } /** * Generate g-machine code for all the supercombinators in the program. * @param module * @param logger * @return CompilerMessage.Severity */ @Override public CompilerMessage.Severity generateSCCode (Module module, CompilerMessageLogger logger) { if (module == null) { throw new IllegalArgumentException("g.CodeGenerator.generateSCCode() cannot have a null module."); } CompilerMessageLogger generateLogger = new MessageLogger(); try { informStatusListeners(StatusListener.SM_GENCODE, module.getName()); currentModule = module; for (final MachineFunction mf : module.getFunctions()) { GMachineFunction gmf = (GMachineFunction)mf; if (gmf.isCodeGenerated()) { continue; } if (gmf.getAliasOf() != null || gmf.getLiteralValue() != null) { gmf.setCodeGenerated(true); continue; } try { generateSCCode (gmf); } catch (CodeGenerationException e) { try { // Note: The code generation could potentially have failed because a foreign type or a foreign function's corresponding Java entity // could not be resolved. (In this case the CodeGenerationException would be wrapping an UnableToResolveForeignEntityException) final Throwable cause = e.getCause(); if (cause instanceof UnableToResolveForeignEntityException) { generateLogger.logMessage(((UnableToResolveForeignEntityException)cause).getCompilerMessage()); } // Code generation aborted. Error generating code for: {cl.getQualifiedName()} generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAborted(gmf.getQualifiedName().getQualifiedName()), e)); } catch (CompilerMessage.AbortCompilation e2) {/* Ignore exceptions generated by the act of logging. */} return generateLogger.getMaxSeverity(); } } fixupPushGlobals (module); } catch (Exception e) { try { if (generateLogger.getNErrors() > 0) { //if an error occurred previously, we continue to compile the program to try to report additional //meaningful compilation errors. However, this can produce spurious exceptions related to the fact //that the program state does not satisfy preconditions because of the initial error(s). We don't //report the spurious exception as an internal coding error. generateLogger.logMessage(new CompilerMessage(new MessageKind.Fatal.UnableToRecoverFromCodeGenErrors(module.getName()))); } else { generateLogger.logMessage(new CompilerMessage(new MessageKind.Fatal.CodeGenerationAbortedDueToInternalCodingError(module.getName()), e)); } } catch (CompilerMessage.AbortCompilation ace) { /* Ignore exceptions generated by the act of logging. */ } } catch (Error e) { try { generateLogger.logMessage(new CompilerMessage(new MessageKind.Error.CodeGenerationAbortedWithException(module.getName(), e))); } catch (CompilerMessage.AbortCompilation ace) { /* Ignore exceptions generated by the act of logging. */ } } finally { if (logger != null) { // Log messages to the passed-in logger. try { logger.logMessages(generateLogger); } catch (CompilerMessage.AbortCompilation e) { /* Ignore exceptions generated by the act of logging. */ } } } return generateLogger.getMaxSeverity(); } /** * Generate supercombinator code * @param gmf the code label for which the supercombinator code should be generated. * @throws CodeGenerationException */ private void generateSCCode(GMachineFunction gmf) throws CodeGenerationException { // We are generating a supercombinator. // Show diagnostics if turned on if (CODEGEN_DIAG) { // DIAG MACHINE_LOGGER.log(Level.FINE, "CodeGen: SC = " + gmf.getName ()); } // Save the supercombinator away in the object to save stack if we recurse this.currentMachineFunction = gmf; InstructionList gp = null; Expression e = gmf.getExpressionForm(); // If this is a DataConstructor for an enumeration data type // we want to simply return as these are treates as int. Expression.PackCons packCons = e.asPackCons(); if (packCons != null) { DataConstructor dc = packCons.getDataConstructor(); if (TypeExpr.isEnumType(dc.getTypeConstructor())) { gmf.setCodeGenerated(true); return; } } // Call the top level scheme // Recursive code generation try { gp = schemeSC(e); } catch (StackOverflowError excp) { // Blown the Java call stack - raise compiler error throw new CodeGenerationException ("Code generation stack recursion too deeply nested, use an iterative code generator", excp); } gp = ko.optimizeCode (gp); // Put instuctions into the MachineFunction Code code = new Code (gp); gmf.setCode(code); gmf.setCodeGenerated(true); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, "\n"); MACHINE_LOGGER.log(Level.FINE, gmf.toString ()); MACHINE_LOGGER.log(Level.FINE, gp.toString ()); MACHINE_LOGGER.log(Level.FINE, "\n"); } } /** * Execute the supercombinator compilation scheme. * Creation date: (12/03/02 9:26:31 PM) * @param e Expression the expression * @return InstructionList the instructions and other data compiled by this scheme * @throws CodeGenerationException */ private InstructionList schemeSC(Expression e) throws CodeGenerationException { InstructionList body = new InstructionList (); // Show diagnostics if turned on if (CODEGEN_DIAG) { // DIAG MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Entering SC compilation scheme with intermediate code:\n" + e); } // Is e a pack constructor? Expression.PackCons packCons = e.asPackCons(); if (packCons != null) { // Generate the pack constructor code and return. DataConstructor dc = packCons.getDataConstructor(); // If this is a supercombinator and we are instrumenting the code. if (System.getProperty("org.openquark.cal.machine.g.call_counts") != null) { body.code (new Instruction.I_Instrument (new Executor.CallCountInfo(currentMachineFunction.getQualifiedName (), "DataConstructor function form counts"))); } // Force the evaluation of any strict arguments. if (dc.hasStrictArgs()) { for (int i = 0; i < dc.getArity(); ++i) { if (dc.isArgStrict(i)) { body.code(new Instruction.I_Push(i)); body.code(Instruction.I_Eval); body.code(new Instruction.I_Pop(1)); } } } if (GENERATE_DEBUG_CODE) { // Add an instruction that will suspend execution if a breakpoint is set // trace a function message, etc. Instruction inst = new Instruction.I_Debug_Processing(currentMachineFunction.getQualifiedName(), currentMachineFunction.getArity()); body.code(inst); } Instruction instruction = Instruction.I_PackCons.makePackCons(dc); body.code (instruction); body.code (new Instruction.I_Update (0)); body.code (Instruction.I_Unwind); } else { // If this is a supercombinator and we are instrumenting the code. if (System.getProperty("org.openquark.cal.machine.g.call_counts") != null) { body.code (new Instruction.I_Instrument (new Executor.CallCountInfo(currentMachineFunction.getQualifiedName (), "Call counts"))); } int arity = currentMachineFunction.getArity(); Map<QualifiedName, Integer> env = new HashMap<QualifiedName, Integer> (); String parameterNames[] = currentMachineFunction.getParameterNames(); for (int i = 0; i < parameterNames.length; ++i) { String parameterName = parameterNames[i]; QualifiedName qn = QualifiedName.make(currentModule.getName(), parameterName); env.put (qn, Integer.valueOf((arity - i))); } //body.code (new Instruction.I_Println("Entering: " + cl.getQualifiedName().toString())); for (int i = 0; i < currentMachineFunction.getArity(); ++i) { if (currentMachineFunction.getParameterStrictness()[i]) { body.code(new Instruction.I_Push(i)); body.code(Instruction.I_Eval); body.code(new Instruction.I_Pop(1)); } } if (GENERATE_DEBUG_CODE) { // Add in an instruction that will generate a trace message with the name of the current function // and the state of its arguments. body.code (new Instruction.I_Debug_Processing(currentMachineFunction.getQualifiedName(), currentMachineFunction.getArity())); } // Invoke the R scheme to compile the body body.code (schemeR(e, env, arity)); } return body; } /** * Execute the R compilation scheme. This generates code to apply a supercombinator to * its arguments. * Creation date: (12/04/02 9:32:17 AM) * @param e Expression the expression * @param p Map: a table linking variable names to stack offsets. * @param d int: the depth of the current execution context minus one (i.e. at this point the number of arguments). * @return InstructionList the instructions and other data compiled by this scheme * @throws CodeGenerationException */ private InstructionList schemeR(Expression e, Map<QualifiedName, Integer> p, int d) throws CodeGenerationException { // R[[ i ]] p d = PUSHVVAL i; UPDATE d; POP d; UNWIND; // R[[ f ]] p d = PUSHGLOBAL f; EVAL; UPDATE d; POP d; UNWIND; // R[[ x ]] p d = PUSH (d - p(x)); EVAL; UPDATE d; POP d; UNWIND; // R[[ Cons E1 E2]] p d = C[[ E2 ]] p d; C[[ E1 ]] p (d+1); CONS; UPDATE d; POP d; UNWIND; // R[[ if Ec Et Ef]] p d = C[[ Ec ]] p d; EVAL; I_COND (R[[ Et ]] p d) (R[[ Ef ]] p d); // R[[ E1 E2]] p d = C[[ E1 E2]] p d; UPDATE d; POP d; UNWIND; // R[[letrec D in E]] p d = CLetrec[[ D ]] p1 d1; R[[ E ]] p1 d1; // where // (p1, d1) = Xr[[ D ]] p d; // Show diagnostics if turned on if (CODEGEN_DIAG) { // DIAG MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Entering R compilation scheme with intermediate code:\n" + e); for (final Map.Entry<QualifiedName, Integer> entry : p.entrySet()) { MACHINE_LOGGER.log(Level.FINE, " " + entry.getKey() + ": " + entry.getValue()); } } InstructionList gp = new InstructionList (); // Is e a literal? Expression.Literal literal = e.asLiteral(); if (literal != null) { // Code a I_PushVVal instruction to push the literal onto the stack. if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Literal:"); } Object val = literal.getLiteral (); if (val instanceof Boolean) { // Booleans are handled as a special case. if (((Boolean)val).booleanValue()) { gp.code (Instruction.I_PushTrue); } else { gp.code (Instruction.I_PushFalse); } } else { gp.code(Instruction.I_PushVVal.makePushVVal(literal.getLiteral())); } appendUpdateCode(gp, d); return gp; } BasicOpTuple basicOpExpressions = BasicOpTuple.isBasicOp(e); if (GENERATE_DEBUG_CODE) { //When we have function tracing enabled, we want to force all primitive operations to be //done as function calls. This will have the effect of ensuring that they get traced when called. if (basicOpExpressions != null && !basicOpExpressions.getName().equals(currentMachineFunction.getQualifiedName())) { basicOpExpressions = null; } } // Is e a basic operation (arithmetic, comparative etc.)? Test only - don't get tuple if (basicOpExpressions != null) { // Unpack the basic op into subexpressions if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " basic:"); } // Code a basic operation int op = basicOpExpressions.getPrimitiveOp (); Instruction instruction = null; if (op == PrimOps.PRIMOP_EAGER) { return schemeR (basicOpExpressions.getArgument(0), p, d); } else if (op == PrimOps.PRIMOP_FOREIGN_FUNCTION) { instruction = new Instruction.I_ForeignFunctionCall (basicOpExpressions.getForeignFunctionInfo()); } else { instruction = new Instruction.I_PrimOp(op); } int nArgs = basicOpExpressions.getNArguments (); if (nArgs < 0) { throw new CodeGenerationException ("Internal Coding Error: Invalid basic operator arity"); } if (op == PrimOps.PRIMOP_CAL_VALUE_TO_OBJECT) { //Prelude.calValueToObject is non-strict in its first argument. gp.code (schemeC (basicOpExpressions.getArgument(0), p, d)); gp.code (instruction); } else { for (int i = 0; i < nArgs; ++i) { gp.code (schemeE (basicOpExpressions.getArgument(i), p, d + i)); } gp.code (instruction); } appendUpdateCode(gp, d); return gp; } // Is e an application of a saturated constructor? if (ConstructorOpTuple.isConstructorOp(e, true) != null) { // Unpack the basic op into subexpressions if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " basic:"); } ConstructorOpTuple constructorOpExpressions = ConstructorOpTuple.isConstructorOp(e, false); DataConstructor dc = constructorOpExpressions.getDataConstructor (); Instruction instruction = Instruction.I_PackCons.makePackCons(dc); int nArgs = constructorOpExpressions.getNArguments (); if (nArgs < 0) { throw new CodeGenerationException ("Internal Coding Error: Invalid constructor operator arity"); } for (int i = 0; i < nArgs; ++i) { gp.code (schemeC (constructorOpExpressions.getArgument(nArgs - i - 1), p, d + i)); } // Force the evaluation of any strict arguments. if (dc.hasStrictArgs()) { for (int i = 0; i < dc.getArity(); ++i) { if (dc.isArgStrict(i)) { gp.code(new Instruction.I_Push(i)); gp.code(Instruction.I_Eval); gp.code(new Instruction.I_Pop(1)); } } } gp.code (instruction); appendUpdateCode (gp, d); return gp; } // Is e a variable? Expression.Var var = e.asVar(); if (var != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Var:"); } // e is a variable, possible addressing modes are: // Push <k> for an argument // PushGlobal <l> for a label (e.g. supercombinator) // Code an Push <k> if we find it's an argument gp.code (schemeC (e, p, d)); appendUpdateCode(gp, d); Integer ei = p.get(var.getName()); if (CODEGEN_DIAG) { if (ei == null) { MACHINE_LOGGER.log(Level.FINE, " Global:"); } else { MACHINE_LOGGER.log(Level.FINE, " local:"); } } return gp; } // Is e a conditional op (if <cond expr> <then expr> <else expr>)? CondTuple conditionExpressions = CondTuple.isCondOp(e); if (conditionExpressions != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " condition:"); } // This is a conditional op. The conditionExpressions tuple holds (kCond, kThen, kElse) expressions // Generate the code for kThen and kElse, as arguments to a new I_Cond instruction gp.code (schemeE (conditionExpressions.getConditionExpression(), p, d)); InstructionList thenPart = schemeR (conditionExpressions.getThenExpression(), p, d); InstructionList elsePart = schemeR (conditionExpressions.getElseExpression(), p, d); Instruction i = new Instruction.I_Cond (new Code (thenPart), new Code(elsePart)); gp.code (i); return gp; } // Is e a switch? Expression.Switch sw = e.asSwitch(); if (sw != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " switch:"); } gp.code (schemeE (sw.getSwitchExpr (), p, d)); // Get the alternatives Expression.Switch.SwitchAlt[] alts = sw.getAlts(); Map<Object, Code> altTagToCodeMap = new HashMap<Object, Code>(); ModuleName moduleName = currentMachineFunction.getQualifiedName().getModuleName(); // Build the code for each branch, save the variable requirement of each // branch as an alternative in gp, for later resolution for (final SwitchAlt alt : alts) { // For now, generate code for each tag. for (final Object altTag : alt.getAltTags()) { String[] vars = getVars(alt, altTag); Map<QualifiedName, Integer> newEnv = argOffset (0, p); for (int j = 0; j < vars.length; ++j) { QualifiedName qn = QualifiedName.make(moduleName, vars [j]); newEnv.put (qn, Integer.valueOf(d + 1 + j)); } // i_split: takes a dc object, tells it to push all (vars.length) fields onto the stack InstructionList altGP = new InstructionList (); altGP.code (new Instruction.I_Split (vars.length)); altGP.code (schemeR(alt.getAltExpr(), newEnv, d + vars.length)); Code code = new Code(altGP); altTagToCodeMap.put(altTag, code); } } ErrorInfo errorInfo = sw.getErrorInfo() == null ? null : toRuntimeErrorInfo(sw.getErrorInfo()); gp.code (new Instruction.I_Switch (altTagToCodeMap, errorInfo)); return gp; } // Is e a data constructor field selection? Expression.DataConsSelection dataConsSelection = e.asDataConsSelection(); if (dataConsSelection != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " selectDC:"); } gp.code (schemeC(dataConsSelection.getDCValueExpr(), p, d)); gp.code (new Instruction.I_LazySelectDCField(dataConsSelection.getDataConstructor(), dataConsSelection.getFieldIndex(), toRuntimeErrorInfo (dataConsSelection.getErrorInfo()))); // // Evaluate the code for the dc-valued expr. //gp.code (schemeE (dataConsSelection.getDCValueExpr(), p, d)); // // Extract the field value onto the stack. // int fieldIndex = dataConsSelection.getFieldIndex(); // ErrorInfo errorInfo = dataConsSelection.getErrorInfo() == null ? null : new ErrorInfo(dataConsSelection.getErrorInfo()); // gp.code (new Instruction.I_SelectDCField (dataConsSelection.getDataConstructor(), fieldIndex, errorInfo)); // // Add a var to the env, and generate code for that var. // Expression.Var varName = dataConsSelection.getVarName(); // QualifiedName varQualifiedName = varName.getName(); // Map newEnv = argOffset (0, p); // newEnv.put (varQualifiedName, JavaPrimitives.makeInteger (d + 1)); // gp.code (schemeR(varName, newEnv, d + 1)); appendUpdateCode (gp, d); return gp; } // Is e a let expression? Expression.Let let = e.asLet(); if (let != null) { // Currently the compiler doesn't differentialte between let and letrec scenarios. // As such we have to treat all lets as letrecs. Expression.Let.LetDefn[] defs = let.getDefns(); EnvAndDepth ead = schemeXr (defs, p, d); InstructionList gprecs = schemeCLetrec (defs, ead.env, ead.depth); gp.code (gprecs); gp.code (schemeR (let.getBody (), ead.env, ead.depth)); return gp; } // Is e a tail recursive call? if (e.asTailRecursiveCall() != null) { // The g-machine doesn't have a specific optimization for tail recursive calls // so we simply handle it as the original fully saturated application and let // the general tail call optimization handle it. return schemeR (e.asTailRecursiveCall().getApplForm(), p, d); } // Is e an application? Expression.Appl appl = e.asAppl(); if (appl != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Application:"); } InstructionList il = schemeRS (e, p, d, 0); if (il != null) { gp.code (il); return gp; } gp.code (schemeC (e, p, d)); appendUpdateCode(gp, d); return gp; } // Is e a record update // e is a record update Expression.RecordUpdate recordUpdate = e.asRecordUpdate(); if (recordUpdate != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record update:"); } gp.code (schemeE (e, p, d)); appendUpdateCode (gp, d); return gp; } // Is e a record extension // e is a record extension Expression.RecordExtension recordExtension = e.asRecordExtension(); if (recordExtension != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record extension:"); } gp.code (schemeE (e, p, d)); appendUpdateCode (gp, d); return gp; } // e is a record selection Expression.RecordSelection recordSelection = e.asRecordSelection(); if (recordSelection != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record selection:"); } gp.code (schemeE (e, p, d)); appendUpdateCode (gp, d); return gp; } // e is a record case Expression.RecordCase recordCase = e.asRecordCase(); if (recordCase != null) { // Strictly compile the condition expression Expression conditionExpr = recordCase.getConditionExpr(); gp.code (schemeE (conditionExpr, p, d)); Map<QualifiedName, Integer> newEnv = argOffset (0, p); QualifiedName recordName = QualifiedName.make(currentMachineFunction.getQualifiedName().getModuleName(), "$recordCase"); newEnv.put (recordName, Integer.valueOf(++d)); //FieldName -> String SortedMap<FieldName, String> fieldBindingVarMap = recordCase.getFieldBindingVarMap(); int recordPos = 0; // This creates, if necessary, a record equivalent to the original record minus the bound fields. String baseRecordPatternVarName = recordCase.getBaseRecordPatternVarName(); if (baseRecordPatternVarName != null && !baseRecordPatternVarName.equals(Expression.RecordCase.WILDCARD_VAR)) { recordPos++; // Create a new record that is the original record minus the bound fields. QualifiedName qn = QualifiedName.make(currentMachineFunction.getQualifiedName().getModuleName(), baseRecordPatternVarName); newEnv.put (qn, Integer.valueOf(++d)); // push the original record gp.code (new Instruction.I_Push(0)); // consume the record on top of the stack and replace with an extended version gp.code (new Instruction.I_ExtendRecord()); // Now remove fields from the record as appropriate. for (final FieldName fieldName : fieldBindingVarMap.keySet()) { gp.code (new Instruction.I_RemoveRecordField(fieldName.getCalSourceForm())); } } // Now push the values for the bound fields onto the stack. for (final Map.Entry<FieldName, String> entry : fieldBindingVarMap.entrySet()) { FieldName fieldName = entry.getKey(); String bindingVarName = entry.getValue(); //ignore anonymous pattern variables. These are guaranteed not to be used //by the result expression, and so don't need to be extracted from the condition record. if (!bindingVarName.equals(Expression.RecordCase.WILDCARD_VAR)) { QualifiedName qn = QualifiedName.make(currentMachineFunction.getQualifiedName().getModuleName(), bindingVarName); newEnv.put(qn, Integer.valueOf(++d)); gp.code (new Instruction.I_Push (recordPos)); gp.code (new Instruction.I_RecordSelection (fieldName.getCalSourceForm())); recordPos++; } } //encode the result expression in the context of the extended variable scope. Expression resultExpr = recordCase.getResultExpr(); gp.code (schemeR (resultExpr, newEnv, d)); appendUpdateCode(gp, d); return gp; } Expression.Cast cast = e.asCast(); if (cast != null) { gp.code (schemeE(cast.getVarToCast(), p, d)); gp.code (new Instruction.I_Cast(getCastType(cast))); appendUpdateCode(gp, d); return gp; } MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Bad exit of R compilation scheme with intermediate code:\n" + e); logEnvironment(p); throw new CodeGenerationException ("Internal Coding Error: unrecognized expression " + e +"."); } /** Execute the RS compilation scheme. Completes the evaluation of an expression, * the top n ribs of which have already been put on the stack. * RS constructs instances of the ribs of E, putting them on the stack and * then unwinds in the same fashion as the R scheme. * @param e * @param p * @param d * @param n * @return null if the given Expression is not handled by the ES scheme, otherwise the IntructionList of generated instructions. * @throws CodeGenerationException */ private InstructionList schemeRS (Expression e, Map<QualifiedName, Integer> p, int d, int n) throws CodeGenerationException { // RS[[ f ]] p d n = PUSHGLOBAL f; MKAP n; UPDATE (d-n); POP (d-n); UNWIND; // RS[[ x ]] p d n = PUSH (d - px); MKAP n; UPDATE (d-n); POP (d-n); UNWIND; // RS[[ HEAD E]] p d n = E[[ E ]] p d; HEAD; MKAP n; UPDATE (d-n); POP (d-n); UNWIND; // RS[[ If Ec Et Ef]] p d n = E[[ Ec]] p d; I_Cond (RS[[ Et ]] p d n, RS[[ Ef ]] p d n); // RS[[ E1 E2 ]] = C[[ E2 ]] p d; RS [[ E1 ]] p (d+1) (n+1); InstructionList gp = new InstructionList (); // Is e a variable? Expression.Var var = e.asVar(); if (var != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Var:"); } // e is a variable, possible addressing modes are: // Push <k> for an argument // PushGlobal <l> for a label (e.g. supercombinator) // Code an Push <k> if we find it's an argument Integer ei = p.get(var.getName()); if (ei == null) { gp.code(new Instruction.I_PushGlobal(var.getName())); //appendRSUpdate (gp, d, n); gp.code (new Instruction.I_Squeeze (n+1, d-n)); gp.code (new Instruction.I_Dispatch (n)); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Global:"); } } else { gp.code (new Instruction.I_Push (d - ei.intValue())); //appendRSUpdate (gp, d, n); gp.code (new Instruction.I_Squeeze (n+1, d-n)); gp.code (new Instruction.I_Dispatch (n)); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " local:"); } } return gp; } // Is e a conditional op (if <cond expr> <then expr> <else expr>)? CondTuple conditionExpressions = CondTuple.isCondOp(e); if (conditionExpressions != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " condition:"); } // This is a conditional op. The conditionExpressions tuple holds (kCond, kThen, kElse) expressions // Generate the code for kThen and kElse, as arguments to a new I_Cond instruction gp.code (schemeE (conditionExpressions.getConditionExpression(), p, d)); InstructionList thenPart = schemeRS (conditionExpressions.getThenExpression(), p, d, n); InstructionList elsePart = schemeRS (conditionExpressions.getElseExpression(), p, d, n); if (thenPart == null || elsePart == null) { // One of the sub expressions could not be handled by schemeRS. Return null and // let the calling code either abort or use an alternate compilation scheme. return null; } Instruction i = new Instruction.I_Cond (new Code (thenPart), new Code(elsePart)); gp.code (i); return gp; } // Is e a switch? Expression.Switch sw = e.asSwitch(); if (sw != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " switch:"); } gp.code (schemeE (sw.getSwitchExpr (), p, d)); // Get the alternatives Expression.Switch.SwitchAlt[] alts = sw.getAlts(); Map<Object, Code> altTagToCodeMap = new HashMap<Object, Code>(); ModuleName moduleName = currentMachineFunction.getQualifiedName().getModuleName(); // Build the code for each branch, save the variable requirement of each // branch as an alternative in gp, for later resolution for (final SwitchAlt alt : alts) { // For now, generate code for each tag. for (final Object altTag : alt.getAltTags()) { String[] vars = getVars(alt, altTag); Map<QualifiedName, Integer> newEnv = argOffset (0, p); for (int j = 0; j < vars.length; ++j) { QualifiedName qn = QualifiedName.make(moduleName, vars [j]); newEnv.put (qn, Integer.valueOf(d + 1 + j)); } InstructionList altGP = new InstructionList (); altGP.code (new Instruction.I_Split (vars.length)); InstructionList il = schemeRS(alt.getAltExpr(), newEnv, d + vars.length, n); if (il == null) { // The alternate could not be compiled by SchemeRS. Return null and let the calling code // decide whether to abort or use an alternate compilation scheme. return null; } altGP.code (il); Code code = new Code(altGP); altTagToCodeMap.put(altTag, code); } } ErrorInfo errorInfo = sw.getErrorInfo() == null ? null : toRuntimeErrorInfo(sw.getErrorInfo()); gp.code (new Instruction.I_Switch (altTagToCodeMap, errorInfo)); return gp; } // Is e an application? Expression.Appl appl = e.asAppl(); if (appl != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Application:"); } // e is an application // Get e1 (LHS) and e2 (RHS) expressions Expression e1 = appl.getE1(); Expression e2 = appl.getE2(); InstructionList gpe2 = schemeC (e2, p, d); InstructionList gpe1 = schemeRS (e1, p, d + 1, n + 1); if (gpe1 == null) { // The RHS could not be handled by schemeRC. Return null and let the calling code // decide whether to abort or use an alternate compilation scheme. return null; } gp.code (gpe2); gp.code (gpe1); return gp; } // The given Expression cannot be handled by the RS scheme. Return null and let the // calling code decide whether to abort or use an alternate scheme. return null; } /** * Execute the E compilation scheme. This generates code to evaluate the * given expression and leave the result on top of the stack. * Creation date: (12/04/02 9:32:17 AM) * @param e Expression the expression * @param p Map: a table linking variable names to stack offsets. * @param d int: the depth of the current execution context minus one (i.e. at this point the number of arguments). * @return InstructionList the instructions and other data compiled by this scheme * @throws CodeGenerationException * @throws CodeGenerationException */ private InstructionList schemeE (Expression e, Map<QualifiedName, Integer> p, int d) throws CodeGenerationException { // E[[ i ]] p d = PUSHVVAL e; // E[[ f ]] p d = PUSHGLOBAL f; EVAL; // E[[ x ]] p d = PUSH (d - p(x)); EVAL; // E[[ Cons E1 E2 ]] p d = C[[ E2 ]] p d; C[[ E1 ]] p (d+1); CONS; // E[[ if Ec Et Ef]] p d = E[[ Ec ]] p d; I_COND (E[[ Et ]] p d) (E[[ Ef ]] p d); // E[[ letrec D in E ]] p d = CLetrec[[ D ]] p' d'; E[[ E ]] p'd'; SLIDE (d'-d); // where // (p', d') = Xr[[ D ]] p d; // E[[ E1 E2 ]] p d = C[[ E1 E2]] p d; EVAL; // Show diagnostics if turned on if (CODEGEN_DIAG) { // DIAG MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Entering E compilation scheme with intermediate code:\n" + e); logEnvironment(p); } InstructionList gp = new InstructionList (); // Is e a literal? Expression.Literal literal = e.asLiteral(); if (literal != null) { // Code a I_PushVVal instruction to push the literal onto the stack. if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Literal:"); } Object val = literal.getLiteral (); if (val instanceof Boolean) { // Booleans are handled as a special case. if (((Boolean)val).booleanValue()) { gp.code (Instruction.I_PushTrue); } else { gp.code (Instruction.I_PushFalse); } } else { gp.code(Instruction.I_PushVVal.makePushVVal(literal.getLiteral())); } return gp; } BasicOpTuple basicOpExpressions = BasicOpTuple.isBasicOp(e); if (GENERATE_DEBUG_CODE) { //When we have function tracing enabled, we want to force all primitive operations to be //done as function calls. This will have the effect of ensuring that they get traced when called. if (basicOpExpressions != null && !basicOpExpressions.getName().equals(currentMachineFunction.getQualifiedName())) { basicOpExpressions = null; } } // Is e a basic operation (arithmetic, comparative etc.)? Test only - don't get tuple if (basicOpExpressions != null) { // Unpack the basic op into subexpressions if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " basic:"); } // Code a basic operation int op = basicOpExpressions.getPrimitiveOp (); Instruction instruction = null; if (op == PrimOps.PRIMOP_EAGER) { return schemeE (basicOpExpressions.getArgument(0), p, d); } else if (op == PrimOps.PRIMOP_FOREIGN_FUNCTION) { instruction = new Instruction.I_ForeignFunctionCall (basicOpExpressions.getForeignFunctionInfo()); } else { instruction = new Instruction.I_PrimOp(op); } int nArgs = basicOpExpressions.getNArguments (); if (nArgs < 0) { throw new CodeGenerationException("Internal Coding Error: Invalid basic operator arity"); } if (op == PrimOps.PRIMOP_CAL_VALUE_TO_OBJECT) { //Prelude.calValueToObject is non-strict in its first argument. gp.code (schemeC (basicOpExpressions.getArgument(0), p, d)); gp.code (instruction); } else { for (int i = 0; i < nArgs; ++i) { gp.code (schemeE (basicOpExpressions.getArgument(i), p, d + i)); } gp.code (instruction); } return gp; } // Is e an application of a saturated constructor? if (ConstructorOpTuple.isConstructorOp(e, true) != null) { // Unpack the basic op into subexpressions if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " basic:"); } ConstructorOpTuple constructorOpExpressions = ConstructorOpTuple.isConstructorOp(e, false); DataConstructor dc = constructorOpExpressions.getDataConstructor (); Instruction instruction = Instruction.I_PackCons.makePackCons(dc); int nArgs = constructorOpExpressions.getNArguments (); if (nArgs < 0) { throw new CodeGenerationException ("Internal Coding Error: Invalid constructor operator arity"); } for (int i = 0; i < nArgs; ++i) { gp.code (schemeC (constructorOpExpressions.getArgument(nArgs - i - 1), p, d + i)); } // Force the evaluation of any strict arguments. if (dc.hasStrictArgs()) { for (int i = 0; i < dc.getArity(); ++i) { if (dc.isArgStrict(i)) { gp.code(new Instruction.I_Push(i)); gp.code(Instruction.I_Eval); gp.code(new Instruction.I_Pop(1)); } } } gp.code (instruction); return gp; } // Is e a variable? Expression.Var var = e.asVar(); if (var != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Var:"); } // e is a variable, possible addressing modes are: // Push <k> for an argument // PushGlobal <l> for a label (e.g. supercombinator) // Code an Push <k> if we find it's an argument Integer ei = p.get(var.getName()); if (ei == null) { // No argument, ENTER LABEL instead - this has to be resolved at runtime gp.code(new Instruction.I_PushGlobal(var.getName())); // If the global is a non-zero arity SC we can skip the I_Eval instruction // since it won't do anything. MachineFunction mf = currentModule.getFunction(var.getName()); if (mf == null || mf.getArity() == 0) { gp.code (Instruction.I_Eval); } if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Global:"); } } else { gp.code (new Instruction.I_Push (d - ei.intValue())); gp.code (Instruction.I_Eval); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " local:"); } } return gp; } // Is e a conditional op (if <cond expr> <then expr> <else expr>)? CondTuple conditionExpressions = CondTuple.isCondOp(e); if (conditionExpressions != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " condition:"); } // This is a conditional op. The conditionExpressions tuple holds (kCond, kThen, kElse) expressions // Generate the code for kThen and kElse, as arguments to a new I_Cond instruction gp.code (schemeE (conditionExpressions.getConditionExpression(), p, d)); InstructionList thenPart = schemeE (conditionExpressions.getThenExpression(), p, d); InstructionList elsePart = schemeE (conditionExpressions.getElseExpression(), p, d); Instruction i = new Instruction.I_Cond (new Code (thenPart), new Code(elsePart)); gp.code (i); return gp; } // Is e a switch? Expression.Switch sw = e.asSwitch(); if (sw != null) { throw new CodeGenerationException ("Encountered a case statement at an inner level. schemeE."); } // Is e a data constructor field selection? Expression.DataConsSelection dataConsSelection = e.asDataConsSelection(); if (dataConsSelection != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " selectDC:"); } gp.code (schemeE(dataConsSelection.getDCValueExpr(), p, d)); gp.code ( new Instruction.I_SelectDCField(dataConsSelection.getDataConstructor(), dataConsSelection.getFieldIndex(), toRuntimeErrorInfo (dataConsSelection.getErrorInfo()))); gp.code(Instruction.I_Eval); return gp; } // Is e a let expression? Expression.Let let = e.asLet(); if (let != null) { // Currently the compiler doesn't differentiate between let and letrec scenarios. // As such we have to treat all lets as letrecs. Expression.Let.LetDefn[] defs = let.getDefns(); EnvAndDepth ead = schemeXr (defs, p, d); InstructionList gprecs = schemeCLetrec (defs, ead.env, ead.depth); gp.code (gprecs); gp.code (schemeE (let.getBody (), ead.env, ead.depth)); if (ead.depth - d > 0) { gp.code (new Instruction.I_Slide (ead.depth - d)); } return gp; } // Is e a tail recursive call? if (e.asTailRecursiveCall() != null) { // The g-machine doesn't have a specific optimization for tail recursive calls // so we simply handle it as the original fully saturated application and let // the general tail call optimization handle it. return schemeE (e.asTailRecursiveCall().getApplForm(), p, d); } // Is e an application? Expression.Appl appl = e.asAppl(); if (appl != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Application:"); } InstructionList il = schemeES (e, p, d+1, 0); if (il != null) { gp.code (new Instruction.I_Alloc (1)); gp.code (il); } else { gp.code (schemeC (e, p, d)); gp.code (Instruction.I_Eval); } return gp; } // Is e a record update Expression.RecordUpdate recordUpdateExpr = e.asRecordUpdate(); if (recordUpdateExpr != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record extension:"); } Expression baseRecordExpr = recordUpdateExpr .getBaseRecordExpr(); //FieldName -> Expression Map<FieldName, Expression> updateFieldValuesMap = recordUpdateExpr .getUpdateFieldValuesMap(); // Strictly evaluate the base record. gp.code(schemeE (baseRecordExpr, p, d)); // Create a new record that is a copy of the base record. gp.code(new Instruction.I_ExtendRecord()); // Put the field values into the new record instance. for (final Map.Entry<FieldName, Expression> entry : updateFieldValuesMap.entrySet()) { FieldName fieldName = entry.getKey(); Expression valueExpr = entry.getValue(); gp.code (schemeC (valueExpr, p, d+1)); gp.code (new Instruction.I_PutRecordField(fieldName.getCalSourceForm())); } return gp; } // Is e a record extension Expression.RecordExtension recordExtensionExpr = e.asRecordExtension(); if (recordExtensionExpr != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record extension:"); } Expression baseRecordExpr = recordExtensionExpr.getBaseRecordExpr(); //FieldName -> Expression Map<FieldName, Expression> extensionFieldsMap = recordExtensionExpr.getExtensionFieldsMap(); if (baseRecordExpr == null) { // No base record so create a new one. gp.code(new Instruction.I_CreateRecord(extensionFieldsMap.size())); } else { // Strictly evaluate the base record. gp.code(schemeE (baseRecordExpr, p, d)); // Create a new record that is a copy of the base record. gp.code(new Instruction.I_ExtendRecord()); } // Put the field values into the new record instance. for (final Map.Entry<FieldName, Expression> entry : extensionFieldsMap.entrySet()) { FieldName fieldName = entry.getKey(); Expression valueExpr = entry.getValue(); gp.code (schemeC (valueExpr, p, d+1)); gp.code (new Instruction.I_PutRecordField(fieldName.getCalSourceForm())); } return gp; } // e is a record selection Expression.RecordSelection recordSelection = e.asRecordSelection(); if (recordSelection != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record selection:"); } Expression recordExpr = recordSelection.getRecordExpr(); String fieldName = recordSelection.getFieldName().getCalSourceForm(); // Evaluate the record to WHNF. gp.code(schemeE(recordExpr, p, d)); // Get the field value from the record. gp.code(new Instruction.I_RecordSelection(fieldName)); gp.code(Instruction.I_Eval); return gp; } // e is a record case Expression.RecordCase recordCase = e.asRecordCase(); if (recordCase != null) { MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Error, encountered a switch statement in the E scheme:\n" + e); throw new CodeGenerationException("CodeGen: Error, encountered a switch statement in the E scheme:\n" + e); } MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Bad exit of E compilation scheme with intermediate code: " + e); logEnvironment(p); throw new CodeGenerationException("CodeGen: Bad exit of E compilation scheme with intermediate code: " + e); } /** * Execute the ES compilation scheme. Completes the evaluation of an expression, * the top n ribs of which have already been put on the stack. * ES constructs instances of the ribs of E, putting them on the stack and * then completes the evaluation in the same was as schemeE. * @param e Expression * @param p Map: a table linking variable names to stack offsets. * @param d int: the depth of the current execution context minus one (i.e. at this point the number of arguments). * @param n int: number of ribs already on stack. * @return null if the given Expression is not handled by the ES scheme. Otherwise: InstructionList the instructions and other data compiled by this scheme * @throws CodeGenerationException */ private InstructionList schemeES (Expression e, Map<QualifiedName, Integer> p, int d, int n) throws CodeGenerationException { // RS[[ f ]] p d n = PUSHGLOBAL f; MKAP n; UPDATE (d-n); POP (d-n); UNWIND; // RS[[ x ]] p d n = PUSH (d - px); MKAP n; UPDATE (d-n); POP (d-n); UNWIND; // RS[[ HEAD E]] p d n = E[[ E ]] p d; HEAD; MKAP n; UPDATE (d-n); POP (d-n); UNWIND; // RS[[ If Ec Et Ef]] p d n = E[[ Ec]] p d; I_Cond (RS[[ Et ]] p d n, RS[[ Ef ]] p d n); // RS[[ E1 E2 ]] = C[[ E2 ]] p d; RS [[ E1 ]] p (d+1) (n+1); InstructionList gp = new InstructionList (); // Is e a variable? Expression.Var var = e.asVar(); if (var != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Var:"); } // e is a variable, possible addressing modes are: // Push <k> for an argument // PushGlobal <l> for a label (e.g. supercombinator) // Code an Push <k> if we find it's an argument Integer ei = p.get(var.getName()); if (ei == null) { gp.code(new Instruction.I_PushGlobal(var.getName())); gp.code (new Instruction.I_Call (n)); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Global:"); } } else { gp.code (new Instruction.I_Push (d - ei.intValue())); gp.code (new Instruction.I_Call (n)); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " local:"); } } return gp; } // Is e a conditional op (if <cond expr> <then expr> <else expr>)? CondTuple conditionExpressions = CondTuple.isCondOp(e); if (conditionExpressions != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " condition:"); } // This is a conditional op. The conditionExpressions tuple holds (kCond, kThen, kElse) expressions // Generate the code for kThen and kElse, as arguments to a new I_Cond instruction gp.code (schemeE (conditionExpressions.getConditionExpression(), p, d)); InstructionList thenPart = schemeES (conditionExpressions.getThenExpression(), p, d, n); InstructionList elsePart = schemeES (conditionExpressions.getElseExpression(), p, d, n); if (thenPart == null || elsePart == null) { // Either the then or else part could not be handled by the RS scheme. Return null and // let the calling code decide whether to abort or use an alternate scheme. return null; } Instruction i = new Instruction.I_Cond (new Code (thenPart), new Code(elsePart)); gp.code (i); return gp; } // Is e a switch? Expression.Switch sw = e.asSwitch(); if (sw != null) { throw new CodeGenerationException ("Encountered a case statement at an inner level. schemeES."); } // Is e an application? Expression.Appl appl = e.asAppl(); if (appl != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Application:"); } // e is an application // Get e1 (LHS) and e2 (RHS) expressions Expression e1 = appl.getE1(); Expression e2 = appl.getE2(); InstructionList gpe2 = schemeC (e2, p, d); InstructionList gpe1 = schemeES (e1, p, d + 1, n + 1); if (gpe1 == null) { // The RHS could not be compiled by the RS scheme. Return null and let // the calling code decide whether to abort or use an alternate scheme. return null; } gp.code (gpe2); gp.code (gpe1); return gp; } // At this point we know that the given Expression cannot be handled by the ES scheme. // Return null and leave it to the calling code to deal with things appropriately. return null; } /** * Execute the C compilation scheme. This generates code construct an * instance of the given expression. * Creation date: (12/04/02 9:32:17 AM) * @param e Expression the expression * @param p Map: a table linking variable names to stack offsets. * @param d int: the depth of the current execution context minus one (i.e. at this point the number of arguments). * @return InstructionList the instructions and other data compiled by this scheme * @throws CodeGenerationException */ private InstructionList schemeC (Expression e, Map<QualifiedName, Integer> p, int d) throws CodeGenerationException { // C[[ i ]] p d = PUSHVVAL i; // C[[ f ]] p d = PUSHGLOBAL f; // C[[ x ]] p d = PUSH (d - p(x)); // C[[ E1 E2 ]] p d = C[[ E2 ]] p d; C[[ E1 ]] p (d + 1); MKAP; // C[[ letrec D in Eb ]] p d = CLetrec[[ D ]] p1 d1; C[[ Eb ]] p1 d1; SLIDE (d1 - d); // where // (p1, d1) = Xr[[ D ]] p d; // Show diagnostics if turned on if (CODEGEN_DIAG) { // DIAG MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Entering C compilation scheme with intermediate code:\n" + e); logEnvironment(p); } if (canIgnoreLaziness(e, p)) { return schemeE(e, p, d); } InstructionList gp = new InstructionList (); // Is e a literal? Expression.Literal literal = e.asLiteral(); if (literal != null) { // Code a I_PushVVal instruction to push the literal onto the stack. if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Literal:"); } Object val = literal.getLiteral (); if (val instanceof Boolean) { // Booleans are handled as a special case. if (((Boolean)val).booleanValue()) { gp.code (Instruction.I_PushTrue); } else { gp.code (Instruction.I_PushFalse); } } else { gp.code(Instruction.I_PushVVal.makePushVVal(literal.getLiteral())); } return gp; } // Is e a basic operation? // There is one basic operation, Prelude.eager, which is treated specially in the C scheme. if (BasicOpTuple.isBasicOp(e) != null) { BasicOpTuple basicOpExpressions = BasicOpTuple.isBasicOp(e); // Code a basic operation int op = basicOpExpressions.getPrimitiveOp (); if (op == PrimOps.PRIMOP_EAGER) { return schemeE (basicOpExpressions.getArgument(0), p, d); } } // Is e a variable? Expression.Var var = e.asVar(); if (var != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Var:"); } // e is a variable, possible addressing modes are: // Push <k> for an argument // PushGlobal <l> for a label (e.g. supercombinator) // Code an Push <k> if we find it's an argument Integer ei = p.get(var.getName()); if (ei == null) { // No argument, ENTER LABEL instead - this has to be resolved at runtime gp.code(new Instruction.I_PushGlobal(var.getName())); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Global:"); } } else { gp.code(new Instruction.I_Push (d - ei.intValue())); if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " local:"); } } return gp; } // Is e a switch? Expression.Switch sw = e.asSwitch(); if (sw != null) { throw new CodeGenerationException ("Encountered a case statement at an inner level. schemeC."); } // Is e a data cons selection? Expression.DataConsSelection dataConsSelection = e.asDataConsSelection(); if (dataConsSelection != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " selectDC:"); } gp.code (schemeC(dataConsSelection.getDCValueExpr(), p, d)); gp.code (new Instruction.I_LazySelectDCField(dataConsSelection.getDataConstructor(), dataConsSelection.getFieldIndex(), toRuntimeErrorInfo (dataConsSelection.getErrorInfo()))); return gp; } // Is e a tail recursive call? if (e.asTailRecursiveCall() != null) { // The g-machine doesn't have a specific optimization for tail recursive calls // so we simply handle it as the original fully saturated application and let // the general tail call optimization handle it. return schemeC (e.asTailRecursiveCall().getApplForm(), p, d); } // Is e an application? Expression.Appl appl = e.asAppl(); if (appl != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Application:"); } // e is an application // Get e1 (LHS) and e2 (RHS) expressions Expression e1 = appl.getE1(); Expression e2 = appl.getE2(); InstructionList gpe2 = schemeC (e2, p, d); InstructionList gpe1 = schemeC (e1, p, d + 1); gp.code (gpe2); gp.code (gpe1); gp.code (new Instruction.I_MkapN ()); return gp; } // Is e a let expression? Expression.Let let = e.asLet(); if (let != null) { // Currently the compiler doesn't differentialte between let and letrec scenarios. // As such we have to treat all lets as letrecs. Expression.Let.LetDefn[] defs = let.getDefns(); EnvAndDepth ead = schemeXr (defs, p, d); InstructionList gprecs = schemeCLetrec (defs, ead.env, ead.depth); gp.code (gprecs); gp.code (schemeC (let.getBody (), ead.env, ead.depth)); if (ead.depth - d > 0) { gp.code (new Instruction.I_Slide (ead.depth - d)); } return gp; } Expression.RecordUpdate recordUpdateExpr = e.asRecordUpdate(); if (recordUpdateExpr != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record update:"); } Expression baseRecordExpr = recordUpdateExpr.getBaseRecordExpr(); //FieldName -> Expression Map<FieldName, Expression> updateFieldValuesMap = recordUpdateExpr.getUpdateFieldValuesMap(); // Lazy evaluation of the base record. gp.code(schemeC (baseRecordExpr, p, d)); // Create an application of the virtual update function to the base record. gp.code(new Instruction.I_LazyRecordUpdate()); // Add field values. In the case of extending an existing record these are // added to the record extension node as if they were further arguments in // the application chain for the virtual function. for (final Map.Entry<FieldName, Expression> entry : updateFieldValuesMap.entrySet()) { FieldName fieldName = entry.getKey(); Expression valueExpr = entry.getValue(); gp.code (schemeC (valueExpr, p, d+1)); gp.code (new Instruction.I_PutRecordField(fieldName.getCalSourceForm())); } return gp; } // Is e a record extension // e is a record extension Expression.RecordExtension recordExtensionExpr = e.asRecordExtension(); if (recordExtensionExpr != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record extension:"); } Expression baseRecordExpr = recordExtensionExpr.getBaseRecordExpr(); //FieldName -> Expression Map<FieldName, Expression> extensionFieldsMap = recordExtensionExpr.getExtensionFieldsMap(); if (baseRecordExpr == null) { // No base record so we create a new one. gp.code(new Instruction.I_CreateRecord(extensionFieldsMap.size())); } else { // Lazy evaluation of the base record. gp.code(schemeC (baseRecordExpr, p, d)); // Create an application of the virtual extension function to the base record. gp.code(new Instruction.I_LazyRecordExtension()); } // Add field values. In the case of extending an existing record these are // added to the record extension node as if they were further arguments in // the application chain for the virtual function. for (final Map.Entry<FieldName, Expression> entry : extensionFieldsMap.entrySet()) { FieldName fieldName = entry.getKey(); Expression valueExpr = entry.getValue(); gp.code (schemeC (valueExpr, p, d+1)); gp.code (new Instruction.I_PutRecordField(fieldName.getCalSourceForm())); } return gp; } // e is a record selection Expression.RecordSelection recordSelection = e.asRecordSelection(); if (recordSelection != null) { if (CODEGEN_DIAG) { MACHINE_LOGGER.log(Level.FINE, " Record selection:"); } Expression recordExpr = recordSelection.getRecordExpr(); String fieldName = recordSelection.getFieldName().getCalSourceForm(); // If we can ignore laziness for the base record (i.e. it can be safely // forced to WHNF) then we can just select the field value, but don't force // the field value to WHNF if (canIgnoreLaziness(recordExpr, p)) { // Evaluate the record to WHNF. gp.code(schemeE(recordExpr, p, d)); // Get the field value from the record. gp.code(new Instruction.I_RecordSelection(fieldName)); } else { // Lazy evaluation of the record. gp.code(schemeC(recordExpr, p, d)); // Create a node representing the application of a virtual selection function to the // record. gp.code(new Instruction.I_LazyRecordSelection(fieldName)); } return gp; } Expression.ErrorInfo errorInfo = e.asErrorInfo(); if (errorInfo != null){ gp.code(Instruction.I_PushVVal.makePushVVal(new ErrorInfo(errorInfo.getTopLevelFunctionName(), errorInfo.getLine(), errorInfo.getColumn()))); return gp; } // e is a record case Expression.RecordCase recordCase = e.asRecordCase(); if (recordCase != null) { MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Error, encountered a switch statement in the E scheme:\n" + e); throw new CodeGenerationException("CodeGen: Error, encountered a switch statement in the E scheme: " + e); } MACHINE_LOGGER.log( Level.FINE, "\nCodeGen: bad exit of C compilation scheme with intermediate code:\n" + e); logEnvironment(p); throw new CodeGenerationException("CodeGen: bad exit of C compilation scheme with intermediate code: " + e); } /** * Generate code to build up the graphs for a set of letrecs. * @param defs Expression.Let.LetDefn[]: the set of letrec definitions. * @param env Map: the current environment. * @param d int: the depth of the current context. * @return InstructionList * @throws CodeGenerationException */ private InstructionList schemeCLetrec (Expression.Let.LetDefn[] defs, Map<QualifiedName, Integer> env, int d) throws CodeGenerationException { // Show diagnostics if turned on if (CODEGEN_DIAG) { // DIAG MACHINE_LOGGER.log(Level.FINE, "\nCodeGen: Entering CLetrec compilation scheme with intermediate code:\n"); for (int i = 0; i < defs.length; ++i) { MACHINE_LOGGER.log(Level.FINE, currentMachineFunction.getQualifiedName().getModuleName() + "." + defs[i].getVar() + " = " + defs[i].getExpr() + "\n"); } logEnvironment(env); } InstructionList gp = new InstructionList (); gp.code (new Instruction.I_Alloc (defs.length)); for (int i = 0; i < defs.length; ++i) { gp.code (schemeC (defs [i].getExpr (), env, d)); gp.code (new Instruction.I_Update (defs.length - i - 1)); } return gp; } /** * Create an autmented environment and context depth for a set of * letrec definitions. * @param defs Expression.Let.LetDefn[]: the set of letrec definitions. * @param env Map: the environment to be augmented. * @param d int: the depth to be augmented. * @return EnvAndDepth */ private EnvAndDepth schemeXr (Expression.Let.LetDefn[] defs, Map<QualifiedName, Integer> env, int d) { // Xr [[ x1 = E1; x2 = E2; ... xn = En; ]] p d = (p[x1 = d + 1; x2 = d + 2; ... xn = d + n;], d + n); EnvAndDepth retVal = new EnvAndDepth (); retVal.depth = d + defs.length; Map<QualifiedName, Integer> newEnv = argOffset (0, env); for (int i = 0; i < defs.length; ++i) { Expression.Let.LetDefn def = defs [i]; QualifiedName qn = QualifiedName.make(currentMachineFunction.getQualifiedName().getModuleName(), def.getVar()); newEnv.put (qn, Integer.valueOf(d + i + 1)); } retVal.env = newEnv; return retVal; } /** * Create a new environment in which all the elements of the given * environment are offset by n. * @param n int: amount to offset by. * @param oldEnv Map: the existing environment. * @return Map. */ private Map<QualifiedName, Integer> argOffset (int n, Map<QualifiedName, Integer> oldEnv) { Map<QualifiedName, Integer> env = new HashMap<QualifiedName, Integer> (); Iterator<Map.Entry<QualifiedName, Integer>> entries = oldEnv.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<QualifiedName, Integer> entry = entries.next(); env.put (entry.getKey(), Integer.valueOf((entry.getValue()).intValue() + n)); } return env; } private void appendUpdateCode (InstructionList gp, int d) { gp.code (new Instruction.I_Update (d)); if (d > 0) { gp.code (new Instruction.I_Pop (d)); } gp.code (Instruction.I_Unwind); } /** * Get the vars for an alt * @param alt the alt for which to retrieve the vars. * @param altTag the altTag for the alt * @return the array of vars, in the order in which they appear in the corresponding data constructor (if any). * 0-length array if the alt's tag is not a data constructor. */ private String[] getVars(Expression.Switch.SwitchAlt alt, Object altTag) { if (altTag instanceof DataConstructor) { DataConstructor dataCons = (DataConstructor)altTag; String[] vars = new String[dataCons.getArity()]; Arrays.fill(vars, Expression.Switch.SwitchAlt.WILDCARD_VAR); if (alt instanceof Expression.Switch.SwitchAlt.Positional) { Map<Integer, String> positionToVarNameMap = ((Expression.Switch.SwitchAlt.Positional)alt).getPositionToVarNameMap(); for (final Map.Entry<Integer, String> entry : positionToVarNameMap.entrySet()) { Integer positionInteger = entry.getKey(); String varName = entry.getValue(); int fieldIndex = positionInteger.intValue(); vars[fieldIndex] = varName; } } else { // Must be matching. Map<FieldName, String> fieldNameToVarNameMap = ((Expression.Switch.SwitchAlt.Matching)alt).getFieldNameToVarNameMap(); for (final Map.Entry<FieldName, String> entry : fieldNameToVarNameMap.entrySet()) { FieldName fieldName = entry.getKey(); String varName = entry.getValue(); int fieldIndex = dataCons.getFieldIndex(fieldName); vars[fieldIndex] = varName; } } return vars; } else { return new String[0]; } } /** * Replace all I_PushGlobal instructions with I_PushGlobalN so that * running code doesn't require lookups into the Program. * @param module * @throws CodeGenerationException */ private void fixupPushGlobals (Module module) throws CodeGenerationException { for (final MachineFunction mf : module.getFunctions()) { GMachineFunction gmf = (GMachineFunction)mf; Code code = gmf.getCode (); if (code == null) { continue; } Instruction[] instructions = code.getInstructions(); for (int i = 0; i < instructions.length; ++i) { Instruction inst = instructions [i]; if (inst instanceof Instruction.I_PushGlobal) { Instruction.I_PushGlobal pg = (Instruction.I_PushGlobal)inst; // We need to handle the supercombinators that are hand-coded and // therefore not in the actual Program object. QualifiedName globalName = pg.getName(); NPrimitiveFunc npf = primitiveFuncMap.get (globalName); if (npf != null) { instructions[i] = new Instruction.I_PushPrimitiveNode(npf); } else { // We can do a fixup of the instruction if the function being pushed is not a CAF. // Non-CAF functions have a single instance of NGlobal associated with them. // CAF functions have multiple instances of NGlobal associated with them, keyed by // the execution context. MachineFunction calledFunction = module.getFunction(globalName); if (calledFunction != null) { if (calledFunction.isForeignFunction() || calledFunction.getArity() > 0) { NGlobal node = ((GMachineFunction)calledFunction).makeNGlobal(null); instructions [i] = new Instruction.I_PushGlobalN (node, !calledFunction.isForeignFunction()); } } else { throw new CodeGenerationException ("Unable to find code label for " + globalName + " when fixing up I_PushGlobal instructions."); } } } } } } /** * Determine if we can ignore laziness for the provided expression. * @param e * @param env * @return true if laziness can be ignored. * @throws CodeGenerationException */ private boolean canIgnoreLaziness (Expression e, Map<QualifiedName, Integer> env) throws CodeGenerationException { // Literal values are already in WHNF if (e.asLiteral() != null) { return true; } // If a var is a non-zero arity SC, a DC, or is already evaluated we can shortcut it. if (e.asVar() != null) { Expression.Var var = e.asVar(); // Data constructors are already in WHNF. if (var.getDataConstructor() != null) { return true; } // If the variable is a strict function argument it is already in WHNF. Integer ei = env.get(var.getName()); if (ei != null) { // This is a function argument check if it is strict. } else { // This is an SC MachineFunction mf = currentModule.getFunction(var.getName()); if (mf != null && mf.getArity() > 0) { return true; } } } // Is e an application of a saturated constructor? ConstructorOpTuple constructorOpTuple = ConstructorOpTuple.isConstructorOp(e, false); if (constructorOpTuple != null) { DataConstructor dc = constructorOpTuple.getDataConstructor (); boolean[] fieldStrictness = new boolean [dc.getArity()]; boolean dcHasStrictFields = false; for (int i = 0; i < dc.getArity(); ++i) { fieldStrictness[i] = dc.isArgStrict(i); if (fieldStrictness[i]) { dcHasStrictFields = true; } } // If there are no strict arguments we can simply create an instance of the DC class. // The simplest way to do this is to treat this DC application as if it were in a strict context. if (!dcHasStrictFields) { return true; } else { // If all strict arguments are already evaluated, or we consider them safe to evaluate (i.e. cheap and // with no side effects) we can treat this as strict. boolean allOK = true; for (int i = 0; i < dc.getArity(); ++i) { if (dc.getArgStrictness()[i] && !canIgnoreLaziness(constructorOpTuple.getArgument(i), env)) { allOK = false; break; } } if (allOK) { return true; } } } // We can shortcut a basic op if it is marked as allowed to // be eagerly evaluated and all arguments can all be shortcut. // Also if the op is Prelude.eager we can shortcut. BasicOpTuple basicOpExpressions = BasicOpTuple.isBasicOp(e); if (basicOpExpressions != null) { if (basicOpExpressions.getPrimitiveOp() == PrimOps.PRIMOP_EAGER) { return true; } QualifiedName opName = basicOpExpressions.getName(); MachineFunction mf = currentModule.getFunction(opName); if (mf == null) { return false; } if (mf.canFunctionBeEagerlyEvaluated()) { int nArgs = basicOpExpressions.getNArguments(); int nWHNFArgs = 0; for (int i = 0; i < nArgs; ++i) { Expression eArg = basicOpExpressions.getArgument(i); if (canIgnoreLaziness(eArg, env)) { nWHNFArgs++; } } if (nArgs == nWHNFArgs) { // All the args are in WHNF so ideally we can ignore laziness for // this primitive operation. However, there are some primitive // ops where an additional condition, that the second argument is // known to not be zero, is required. String unqualifiedOpName = opName.getUnqualifiedName(); if (opName.getModuleName().equals(CAL_Prelude.MODULE_NAME) && (unqualifiedOpName.equals("divideLong") || unqualifiedOpName.equals("remainderLong") || unqualifiedOpName.equals("divideInt") || unqualifiedOpName.equals("remainderInt") || unqualifiedOpName.equals("divideShort") || unqualifiedOpName.equals("remainderShort") || unqualifiedOpName.equals("divideByte") || unqualifiedOpName.equals("remainderByte"))) { // Check that the second argument is a non zero literal. Expression arg = basicOpExpressions.getArgument(1); if (arg.asLiteral() != null) { if (unqualifiedOpName.equals("divideLong") || unqualifiedOpName.equals("remainderLong")) { Long l = (Long)arg.asLiteral().getLiteral(); return l.longValue() != 0; } else if (unqualifiedOpName.equals("divideInt") || unqualifiedOpName.equals("remainderInt")) { Integer i = (Integer)arg.asLiteral().getLiteral(); return i.intValue() != 0; } else if (unqualifiedOpName.equals("divideShort") || unqualifiedOpName.equals("remainderShort")) { Short shortValue = (Short)arg.asLiteral().getLiteral(); return shortValue.shortValue() != 0; } else if (unqualifiedOpName.equals("divideByte") || unqualifiedOpName.equals("remainderByte")) { Byte byteValue = (Byte)arg.asLiteral().getLiteral(); return byteValue.byteValue() != 0; } else { throw new IllegalStateException(); } } else { return false; } } else { return true; } } else { return false; } } } basicOpExpressions = BasicOpTuple.isAndOr (e); if (basicOpExpressions != null) { // Code a basic operation int nArgs = basicOpExpressions.getNArguments (); int nWHNFArgs = 0; for (int i = 0; i < nArgs; ++i) { Expression eArg = basicOpExpressions.getArgument(i); if (canIgnoreLaziness(eArg, env)) { nWHNFArgs++; } } if (nArgs == nWHNFArgs) { return true; } } // If e is a fully saturated application of a function tagged for optimization and // all the arguments are in WHNF or can have laziness ignored we can // ignore laziness for the application. if (e.asAppl() != null) { Expression[] chain = appChain(e.asAppl()); if (chain[0].asVar() != null) { // Get the supercombinator on the left end of the chain. Expression.Var scVar = chain[0].asVar(); if (scVar != null) { // Check if this supercombinator is one we should try to optimize. MachineFunction mf = currentModule.getFunction(scVar.getName()); if (mf != null && mf.canFunctionBeEagerlyEvaluated()) { // Now determine the arity of the SC. int calledArity = mf.getArity(); // Check to see if we can ignore laziness for all the arguments. if (chain.length - 1 == calledArity) { int nWHNFArgs = 0; for (int i = 0; i < calledArity; ++i) { if (canIgnoreLaziness(chain[i+1], env)) { nWHNFArgs++; } } if (nWHNFArgs == calledArity) { return true; } } } } } } // Is e an application of a zero arity constructor. if (ConstructorOpTuple.isConstructorOp(e, true) != null) { ConstructorOpTuple constructorOpExpressions = ConstructorOpTuple.isConstructorOp(e, false); DataConstructor dc = constructorOpExpressions.getDataConstructor (); if (dc.getArity() == 0){ return true; } } // Is e a DataConsFieldSelection where the laziness can be ignored for the data constructor // expression and the field is strict. if (e.asDataConsSelection() != null) { Expression.DataConsSelection dcs = (Expression.DataConsSelection)e; if (dcs.getDataConstructor().isArgStrict(dcs.getFieldIndex()) && canIgnoreLaziness(dcs.getDCValueExpr(), env)) { return true; } } // 'if a then b else c' where laziness can be ignore for a, b, and c. CondTuple conditionExpressions = CondTuple.isCondOp(e); if (conditionExpressions != null) { Expression condExpr = conditionExpressions.getConditionExpression(); Expression thenExpr = conditionExpressions.getThenExpression(); Expression elseExpr = conditionExpressions.getElseExpression(); if (canIgnoreLaziness(condExpr, env) && canIgnoreLaziness(thenExpr, env) && canIgnoreLaziness(elseExpr, env)) { return true; } } // We can compile a record extension strictly if the base record is null or // laziness can be ignored for the base record. This is safe because while // strict compilation generates code that creates a new record object none // of the fields will be comiled differently, thus preserving laziness. if (e.asRecordExtension() != null) { Expression.RecordExtension re = (Expression.RecordExtension)e; return re.getBaseRecordExpr() == null || canIgnoreLaziness(re.getBaseRecordExpr(), env); } // We can compile a record update strictly if we can ignore laziness for the base // record. This is safe because while // strict compilation generates code that creates a new record object none // of the fields will be comiled differently, thus preserving laziness. if (e.asRecordUpdate() != null) { Expression.RecordUpdate ru = (Expression.RecordUpdate)e; return canIgnoreLaziness(ru.getBaseRecordExpr(), env); } /////////////////// // Note: we can't ignore laziness for a record selection/case even if // laziness can be ignored for the base record expression since strict // compilation would force the evaluation of the field value and change // laziness. // However there is a less general optimization that can be applied in this // situations. See schemeC(). return false; } /** * Place an application chain into a more easily manageable format. * @param root * @return Expression[] */ private Expression[] appChain (Expression.Appl root) { // Walk down the left branch. Expression c = root; int nArgs = 0; while (c instanceof Expression.Appl) { nArgs++; c = ((Expression.Appl)c).getE1(); } Expression[] chain = new Expression [nArgs + 1]; chain[0] = c; c = root; for (int i = nArgs; i >= 1; i--) { chain[i] = ((Expression.Appl)c).getE2(); c = ((Expression.Appl)c).getE1(); } return chain; } private static class EnvAndDepth { int depth; Map<QualifiedName, Integer> env; } /** * A log message formatter that simply outputs the message of the log record. * Used by ICE to print message to the console without any additional info. * @author Frank Worsley */ private static class ConsoleFormatter extends Formatter { /** * @see java.util.logging.Formatter#format(java.util.logging.LogRecord) */ @Override public String format(LogRecord record) { return record.getMessage() + "\n"; } } private static ErrorInfo toRuntimeErrorInfo(final Expression.ErrorInfo errorInfo) { return new ErrorInfo(errorInfo.getTopLevelFunctionName(), errorInfo.getLine(), errorInfo.getColumn()); } /** * Returns the Class object corresponding to the cast type in a cast expression. If the Class object could not be resolved, * a CodeGenerationException is thrown. * @param castExpression the cast expression. * @return the Class object corresponding to the cast type in a cast expression. * @throws CodeGenerationException if the Class object could not be resolved. */ private static Class<?> getCastType(final Expression.Cast castExpression) throws CodeGenerationException { try { return castExpression.getCastType(); } catch (UnableToResolveForeignEntityException e) { throw new CodeGenerationException("Failed to resolve foreign type for Expression.Cast.", e); } } private static void logEnvironment (Map<QualifiedName, Integer> env) { for (final Map.Entry<QualifiedName, Integer> entry : env.entrySet()) { QualifiedName key = entry.getKey(); Integer val = entry.getValue(); MACHINE_LOGGER.log(Level.FINE, " " + key + ": " + val); } } }