예제 #1
0
  static void compile(ApplyExp exp, Compilation comp, Target target, boolean checkInlineable) {
    int args_length = exp.args.length;
    Expression exp_func = exp.func;
    LambdaExp func_lambda = null;
    String func_name = null;
    Declaration owner = null;
    Object quotedValue = null;
    if (exp_func instanceof LambdaExp) {
      func_lambda = (LambdaExp) exp_func;
      func_name = func_lambda.getName();
      if (func_name == null) func_name = "<lambda>";
    } else if (exp_func instanceof ReferenceExp) {
      ReferenceExp func_ref = (ReferenceExp) exp_func;
      owner = func_ref.contextDecl();
      Declaration func_decl = func_ref.binding;
      while (func_decl != null && func_decl.isAlias() && func_decl.value instanceof ReferenceExp) {
        func_ref = (ReferenceExp) func_decl.value;
        if (owner != null || func_decl.needsContext() || func_ref.binding == null) break;
        func_decl = func_ref.binding;
        owner = func_ref.contextDecl();
      }
      if (!func_decl.getFlag(Declaration.IS_UNKNOWN)) {
        Expression value = func_decl.getValue();
        func_name = func_decl.getName();
        if (value != null && value instanceof LambdaExp) func_lambda = (LambdaExp) value;
        if (value != null && value instanceof QuoteExp) quotedValue = ((QuoteExp) value).getValue();
      }
    } else if (exp_func instanceof QuoteExp) {
      quotedValue = ((QuoteExp) exp_func).getValue();
    }
    if (checkInlineable && quotedValue instanceof Procedure) {
      Procedure proc = (Procedure) quotedValue;
      if (target instanceof IgnoreTarget && proc.isSideEffectFree()) {
        for (int i = 0; i < args_length; i++) exp.args[i].compile(comp, target);
        return;
      }
      try {
        if (inlineCompile(proc, exp, comp, target)) return;
      } catch (Throwable ex) {
        comp.getMessages()
            .error('e', "caught exception in inline-compiler for " + quotedValue + " - " + ex, ex);
        return;
      }
    }

    gnu.bytecode.CodeAttr code = comp.getCode();
    Method method;

    if (func_lambda != null) {
      if ((func_lambda.max_args >= 0 && args_length > func_lambda.max_args)
          || args_length < func_lambda.min_args)
        // This is supposed to get caught by InlineCalls.
        throw new Error("internal error - wrong number of parameters for " + func_lambda);
      int conv = func_lambda.getCallConvention();
      // Mostly duplicates logic with LambdaExp.validateApply.
      // See comment there.
      if (comp.inlineOk(func_lambda)
          && (conv <= Compilation.CALL_WITH_CONSUMER
              || (conv == Compilation.CALL_WITH_TAILCALLS && !exp.isTailCall()))
          && (method = func_lambda.getMethod(args_length)) != null) {
        PrimProcedure pproc = new PrimProcedure(method, func_lambda);
        boolean is_static = method.getStaticFlag();
        boolean extraArg = false;
        // ?? Procedure.checkArgCount(this, args.length); // FIXME
        if (!is_static || func_lambda.declareClosureEnv() != null) {
          if (is_static) extraArg = true;
          if (comp.curLambda == func_lambda) // Recursive call.
          code.emitLoad(
                func_lambda.closureEnv != null ? func_lambda.closureEnv : func_lambda.thisVariable);
          else if (owner != null) owner.load(null, 0, comp, Target.pushObject);
          else func_lambda.getOwningLambda().loadHeapFrame(comp);
        }

        pproc.compile(extraArg ? Type.voidType : null, exp, comp, target);
        return;
      }
    }

    /*
       if (comp.usingCPStyle())
         {
      {
        Label l = new Label(code);
        gnu.bytecode.SwitchState fswitch = comp.fswitch;
        int pc = fswitch.getMaxValue() + 1;
        fswitch.addCase(pc, code, l);
               exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));
        comp.loadCallContext();

        // Emit: context->pc = pc.
        comp.loadCallContext();
        code.emitPushInt(pc);
        code.emitPutField(Compilation.pcCallContextField);
        code.emitInvokeVirtual(Compilation.applyCpsMethod);

        // emit[save java stack, if needed]
        Type[] stackTypes = code.saveStackTypeState(false);
        java.util.Stack stackFields = new java.util.Stack();
        if (stackTypes != null)
          {
    	for (int i = stackTypes.length;  --i >= 0; )
    	  {
    	    Field fld = comp.allocLocalField (stackTypes[i], null);
    	    code.emitPushThis();
    	    code.emitSwap();
    	    code.emitPutField(fld);
    	    stackFields.push(fld);
    	  }
          }

        code.emitReturn();
        l.define(code);

        // emit[restore java stack, if needed]
        if (stackTypes != null)
          {
    	for (int i = stackTypes.length;  --i >= 0; )
    	  {
    	    Field fld = (Field) stackFields.pop();
    	    code.emitPushThis();
    	    code.emitGetField(fld);
    	    comp.freeLocalField(fld);
    	  }
          }

        // FIXME
        // Load result from stack.value to target.
        comp.loadCallContext();
        code.emitGetField(comp.valueCallContextField);
        target.compileFromStack(comp, Type.pointer_type);
      }
    return;
         }
       */

    // Check for tail-recursion.
    boolean tail_recurse = exp.isTailCall() && func_lambda != null && func_lambda == comp.curLambda;

    if (func_lambda != null
        && func_lambda.getInlineOnly()
        && !tail_recurse
        && func_lambda.min_args == args_length) {
      pushArgs(func_lambda, exp.args, null, comp);
      if (func_lambda.getFlag(LambdaExp.METHODS_COMPILED)) {
        popParams(code, func_lambda, null, false);
        code.emitTailCall(false, func_lambda.getVarScope());
        return;
      }
      func_lambda.flags |= LambdaExp.METHODS_COMPILED;
      LambdaExp saveLambda = comp.curLambda;
      comp.curLambda = func_lambda;
      func_lambda.allocChildClasses(comp);
      func_lambda.allocParameters(comp);
      popParams(code, func_lambda, null, false);
      func_lambda.enterFunction(comp);
      func_lambda.body.compileWithPosition(comp, target);
      func_lambda.compileEnd(comp);
      func_lambda.generateApplyMethods(comp);
      code.popScope();
      comp.curLambda = saveLambda;
      return;
    }

    if (comp.curLambda.isHandlingTailCalls()
        && (exp.isTailCall() || target instanceof ConsumerTarget)
        && !comp.curLambda.getInlineOnly()) {
      ClassType typeContext = Compilation.typeCallContext;
      exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));
      // evaluate args to frame-locals vars;  // may recurse!
      if (args_length <= 4) {
        for (int i = 0; i < args_length; ++i)
          exp.args[i].compileWithPosition(comp, Target.pushObject);
        comp.loadCallContext();
        code.emitInvoke(
            Compilation.typeProcedure.getDeclaredMethod("check" + args_length, args_length + 1));
      } else {
        compileToArray(exp.args, comp);
        comp.loadCallContext();
        code.emitInvoke(Compilation.typeProcedure.getDeclaredMethod("checkN", 2));
      }
      if (exp.isTailCall()) {
        code.emitReturn();
      } else if (((ConsumerTarget) target).isContextTarget()) {
        comp.loadCallContext();
        code.emitInvoke(typeContext.getDeclaredMethod("runUntilDone", 0));
      } else {
        comp.loadCallContext();
        code.emitLoad(((ConsumerTarget) target).getConsumerVariable());
        code.emitInvoke(typeContext.getDeclaredMethod("runUntilValue", 1));
      }
      return;
    }

    if (!tail_recurse) exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));

    boolean toArray =
        (tail_recurse ? func_lambda.min_args != func_lambda.max_args : args_length > 4);
    int[] incValues = null; // Increments if we can use iinc.
    if (toArray) {
      compileToArray(exp.args, comp);
      method = Compilation.applyNmethod;
    } else if (tail_recurse) {
      incValues = new int[exp.args.length];
      pushArgs(func_lambda, exp.args, incValues, comp);
      method = null;
    } else {
      for (int i = 0; i < args_length; ++i) {
        exp.args[i].compileWithPosition(comp, Target.pushObject);
        if (!code.reachableHere()) break;
      }
      method = Compilation.applymethods[args_length];
    }
    if (!code.reachableHere()) {
      comp.error('e', "unreachable code");
      return;
    }
    if (tail_recurse) {
      popParams(code, func_lambda, incValues, toArray);
      code.emitTailCall(false, func_lambda.getVarScope());
      return;
    }
    code.emitInvokeVirtual(method);
    target.compileFromStack(comp, Type.pointer_type);
  }