1 /*
   2  * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.lir.gen;
  24 
  25 import static jdk.vm.ci.code.ValueUtil.asAllocatableValue;
  26 import static jdk.vm.ci.code.ValueUtil.isAllocatableValue;
  27 import static jdk.vm.ci.code.ValueUtil.isLegal;
  28 import static jdk.vm.ci.code.ValueUtil.isStackSlot;
  29 import static org.graalvm.compiler.lir.LIRValueUtil.asConstant;
  30 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
  31 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
  32 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
  33 
  34 import java.util.ArrayList;
  35 import java.util.List;
  36 
  37 import org.graalvm.compiler.asm.Label;
  38 import org.graalvm.compiler.core.common.LIRKind;
  39 import org.graalvm.compiler.core.common.calc.Condition;
  40 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  41 import org.graalvm.compiler.core.common.spi.CodeGenProviders;
  42 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
  43 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
  44 import org.graalvm.compiler.core.common.spi.LIRKindTool;
  45 import org.graalvm.compiler.core.common.type.Stamp;
  46 import org.graalvm.compiler.debug.GraalError;
  47 import org.graalvm.compiler.debug.TTY;
  48 import org.graalvm.compiler.graph.NodeSourcePosition;
  49 import org.graalvm.compiler.lir.ConstantValue;
  50 import org.graalvm.compiler.lir.LIR;
  51 import org.graalvm.compiler.lir.LIRFrameState;
  52 import org.graalvm.compiler.lir.LIRInstruction;
  53 import org.graalvm.compiler.lir.LIRVerifier;
  54 import org.graalvm.compiler.lir.LabelRef;
  55 import org.graalvm.compiler.lir.StandardOp;
  56 import org.graalvm.compiler.lir.StandardOp.BlockEndOp;
  57 import org.graalvm.compiler.lir.StandardOp.LabelOp;
  58 import org.graalvm.compiler.lir.StandardOp.SaveRegistersOp;
  59 import org.graalvm.compiler.lir.SwitchStrategy;
  60 import org.graalvm.compiler.lir.Variable;
  61 import org.graalvm.compiler.options.Option;
  62 import org.graalvm.compiler.options.OptionKey;
  63 import org.graalvm.compiler.options.OptionType;
  64 import org.graalvm.compiler.options.OptionValues;
  65 
  66 import jdk.vm.ci.code.CallingConvention;
  67 import jdk.vm.ci.code.CodeCacheProvider;
  68 import jdk.vm.ci.code.Register;
  69 import jdk.vm.ci.code.RegisterAttributes;
  70 import jdk.vm.ci.code.StackSlot;
  71 import jdk.vm.ci.code.TargetDescription;
  72 import jdk.vm.ci.meta.AllocatableValue;
  73 import jdk.vm.ci.meta.Constant;
  74 import jdk.vm.ci.meta.JavaConstant;
  75 import jdk.vm.ci.meta.JavaKind;
  76 import jdk.vm.ci.meta.MetaAccessProvider;
  77 import jdk.vm.ci.meta.PlatformKind;
  78 import jdk.vm.ci.meta.Value;
  79 import jdk.vm.ci.meta.ValueKind;
  80 
  81 /**
  82  * This class traverses the HIR instructions and generates LIR instructions from them.
  83  */
  84 public abstract class LIRGenerator implements LIRGeneratorTool {
  85 
  86     public static class Options {
  87         // @formatter:off
  88         @Option(help = "Print HIR along side LIR as the latter is generated", type = OptionType.Debug)
  89         public static final OptionKey<Boolean> PrintIRWithLIR = new OptionKey<>(false);
  90         @Option(help = "The trace level for the LIR generator", type = OptionType.Debug)
  91         public static final OptionKey<Integer> TraceLIRGeneratorLevel = new OptionKey<>(0);
  92         // @formatter:on
  93     }
  94 
  95     private final LIRKindTool lirKindTool;
  96 
  97     private final CodeGenProviders providers;
  98 
  99     private AbstractBlockBase<?> currentBlock;
 100 
 101     private LIRGenerationResult res;
 102 
 103     protected final ArithmeticLIRGenerator arithmeticLIRGen;
 104     private final MoveFactory moveFactory;
 105 
 106     private final boolean printIrWithLir;
 107     private final int traceLIRGeneratorLevel;
 108 
 109     public LIRGenerator(LIRKindTool lirKindTool, ArithmeticLIRGenerator arithmeticLIRGen, MoveFactory moveFactory, CodeGenProviders providers, LIRGenerationResult res) {
 110         this.lirKindTool = lirKindTool;
 111         this.arithmeticLIRGen = arithmeticLIRGen;
 112         this.res = res;
 113         this.providers = providers;
 114         OptionValues options = res.getLIR().getOptions();
 115         this.printIrWithLir = !TTY.isSuppressed() && Options.PrintIRWithLIR.getValue(options);
 116         this.traceLIRGeneratorLevel = TTY.isSuppressed() ? 0 : Options.TraceLIRGeneratorLevel.getValue(options);
 117 
 118         assert arithmeticLIRGen.lirGen == null;
 119         arithmeticLIRGen.lirGen = this;
 120         this.moveFactory = moveFactory;
 121     }
 122 
 123     @Override
 124     public ArithmeticLIRGeneratorTool getArithmetic() {
 125         return arithmeticLIRGen;
 126     }
 127 
 128     @Override
 129     public MoveFactory getMoveFactory() {
 130         return moveFactory;
 131     }
 132 
 133     private MoveFactory spillMoveFactory;
 134 
 135     @Override
 136     public MoveFactory getSpillMoveFactory() {
 137         if (spillMoveFactory == null) {
 138             boolean verify = false;
 139             assert (verify = true) == true;
 140             if (verify) {
 141                 spillMoveFactory = new VerifyingMoveFactory(moveFactory);
 142             } else {
 143                 spillMoveFactory = moveFactory;
 144             }
 145         }
 146         return spillMoveFactory;
 147     }
 148 
 149     @Override
 150     public LIRKind getValueKind(JavaKind javaKind) {
 151         return LIRKind.fromJavaKind(target().arch, javaKind);
 152     }
 153 
 154     @Override
 155     public TargetDescription target() {
 156         return getCodeCache().getTarget();
 157     }
 158 
 159     @Override
 160     public CodeGenProviders getProviders() {
 161         return providers;
 162     }
 163 
 164     @Override
 165     public MetaAccessProvider getMetaAccess() {
 166         return providers.getMetaAccess();
 167     }
 168 
 169     @Override
 170     public CodeCacheProvider getCodeCache() {
 171         return providers.getCodeCache();
 172     }
 173 
 174     @Override
 175     public ForeignCallsProvider getForeignCalls() {
 176         return providers.getForeignCalls();
 177     }
 178 
 179     public LIRKindTool getLIRKindTool() {
 180         return lirKindTool;
 181     }
 182 
 183     /**
 184      * Hide {@link #nextVariable()} from other users.
 185      */
 186     public abstract static class VariableProvider {
 187         private int numVariables;
 188 
 189         public int numVariables() {
 190             return numVariables;
 191         }
 192 
 193         private int nextVariable() {
 194             return numVariables++;
 195         }
 196     }
 197 
 198     @Override
 199     public Variable newVariable(ValueKind<?> valueKind) {
 200         return new Variable(valueKind, ((VariableProvider) res.getLIR()).nextVariable());
 201     }
 202 
 203     @Override
 204     public RegisterAttributes attributes(Register register) {
 205         return res.getFrameMapBuilder().getRegisterConfig().getAttributesMap()[register.number];
 206     }
 207 
 208     @Override
 209     public Variable emitMove(Value input) {
 210         assert !(input instanceof Variable) : "Creating a copy of a variable via this method is not supported (and potentially a bug): " + input;
 211         Variable result = newVariable(input.getValueKind());
 212         emitMove(result, input);
 213         return result;
 214     }
 215 
 216     @Override
 217     public void emitMove(AllocatableValue dst, Value src) {
 218         append(moveFactory.createMove(dst, src));
 219     }
 220 
 221     @Override
 222     public void emitMoveConstant(AllocatableValue dst, Constant src) {
 223         append(moveFactory.createLoad(dst, src));
 224     }
 225 
 226     @Override
 227     public Value emitConstant(LIRKind kind, Constant constant) {
 228         if (moveFactory.canInlineConstant(constant)) {
 229             return new ConstantValue(toRegisterKind(kind), constant);
 230         } else {
 231             return emitLoadConstant(kind, constant);
 232         }
 233     }
 234 
 235     @Override
 236     public Value emitJavaConstant(JavaConstant constant) {
 237         return emitConstant(getValueKind(constant.getJavaKind()), constant);
 238     }
 239 
 240     @Override
 241     public AllocatableValue emitLoadConstant(ValueKind<?> kind, Constant constant) {
 242         Variable result = newVariable(kind);
 243         emitMoveConstant(result, constant);
 244         return result;
 245     }
 246 
 247     @Override
 248     public AllocatableValue asAllocatable(Value value) {
 249         if (isAllocatableValue(value)) {
 250             return asAllocatableValue(value);
 251         } else if (isConstantValue(value)) {
 252             return emitLoadConstant(value.getValueKind(), asConstant(value));
 253         } else {
 254             return emitMove(value);
 255         }
 256     }
 257 
 258     @Override
 259     public Variable load(Value value) {
 260         if (!isVariable(value)) {
 261             return emitMove(value);
 262         }
 263         return (Variable) value;
 264     }
 265 
 266     @Override
 267     public Value loadNonConst(Value value) {
 268         if (isConstantValue(value) && !moveFactory.canInlineConstant(asConstant(value))) {
 269             return emitMove(value);
 270         }
 271         return value;
 272     }
 273 
 274     /**
 275      * Determines if only oop maps are required for the code generated from the LIR.
 276      */
 277     @Override
 278     public boolean needOnlyOopMaps() {
 279         return false;
 280     }
 281 
 282     /**
 283      * Gets the ABI specific operand used to return a value of a given kind from a method.
 284      *
 285      * @param javaKind the kind of value being returned
 286      * @param valueKind the backend type of the value being returned
 287      * @return the operand representing the ABI defined location used return a value of kind
 288      *         {@code kind}
 289      */
 290     @Override
 291     public AllocatableValue resultOperandFor(JavaKind javaKind, ValueKind<?> valueKind) {
 292         Register reg = res.getFrameMapBuilder().getRegisterConfig().getReturnRegister(javaKind);
 293         assert target().arch.canStoreValue(reg.getRegisterCategory(), valueKind.getPlatformKind()) : reg.getRegisterCategory() + " " + valueKind.getPlatformKind();
 294         return reg.asValue(valueKind);
 295     }
 296 
 297     NodeSourcePosition currentPosition;
 298 
 299     @Override
 300     public void setSourcePosition(NodeSourcePosition position) {
 301         currentPosition = position;
 302     }
 303 
 304     @Override
 305     public <I extends LIRInstruction> I append(I op) {
 306         LIR lir = res.getLIR();
 307         if (printIrWithLir) {
 308             TTY.println(op.toStringWithIdPrefix());
 309             TTY.println();
 310         }
 311         assert LIRVerifier.verify(op);
 312         ArrayList<LIRInstruction> lirForBlock = lir.getLIRforBlock(getCurrentBlock());
 313         op.setPosition(currentPosition);
 314         lirForBlock.add(op);
 315         return op;
 316     }
 317 
 318     @Override
 319     public boolean hasBlockEnd(AbstractBlockBase<?> block) {
 320         ArrayList<LIRInstruction> ops = getResult().getLIR().getLIRforBlock(block);
 321         if (ops.size() == 0) {
 322             return false;
 323         }
 324         return ops.get(ops.size() - 1) instanceof BlockEndOp;
 325     }
 326 
 327     private final class BlockScopeImpl extends BlockScope {
 328 
 329         private BlockScopeImpl(AbstractBlockBase<?> block) {
 330             currentBlock = block;
 331         }
 332 
 333         private void doBlockStart() {
 334             if (printIrWithLir) {
 335                 TTY.print(currentBlock.toString());
 336             }
 337 
 338             // set up the list of LIR instructions
 339             assert res.getLIR().getLIRforBlock(currentBlock) == null : "LIR list already computed for this block";
 340             res.getLIR().setLIRforBlock(currentBlock, new ArrayList<LIRInstruction>());
 341 
 342             append(new LabelOp(new Label(currentBlock.getId()), currentBlock.isAligned()));
 343 
 344             if (traceLIRGeneratorLevel >= 1) {
 345                 TTY.println("BEGIN Generating LIR for block B" + currentBlock.getId());
 346             }
 347         }
 348 
 349         private void doBlockEnd() {
 350             if (traceLIRGeneratorLevel >= 1) {
 351                 TTY.println("END Generating LIR for block B" + currentBlock.getId());
 352             }
 353 
 354             if (printIrWithLir) {
 355                 TTY.println();
 356             }
 357             currentBlock = null;
 358         }
 359 
 360         @Override
 361         public AbstractBlockBase<?> getCurrentBlock() {
 362             return currentBlock;
 363         }
 364 
 365         @Override
 366         public void close() {
 367             doBlockEnd();
 368         }
 369 
 370     }
 371 
 372     @Override
 373     public final BlockScope getBlockScope(AbstractBlockBase<?> block) {
 374         BlockScopeImpl blockScope = new BlockScopeImpl(block);
 375         blockScope.doBlockStart();
 376         return blockScope;
 377     }
 378 
 379     @Override
 380     public void emitIncomingValues(Value[] params) {
 381         ((LabelOp) res.getLIR().getLIRforBlock(getCurrentBlock()).get(0)).setIncomingValues(params);
 382     }
 383 
 384     @Override
 385     public abstract void emitJump(LabelRef label);
 386 
 387     @Override
 388     public abstract void emitCompareBranch(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, LabelRef trueDestination, LabelRef falseDestination,
 389                     double trueDestinationProbability);
 390 
 391     @Override
 392     public abstract void emitOverflowCheckBranch(LabelRef overflow, LabelRef noOverflow, LIRKind cmpKind, double overflowProbability);
 393 
 394     @Override
 395     public abstract void emitIntegerTestBranch(Value left, Value right, LabelRef trueDestination, LabelRef falseDestination, double trueSuccessorProbability);
 396 
 397     @Override
 398     public abstract Variable emitConditionalMove(PlatformKind cmpKind, Value leftVal, Value right, Condition cond, boolean unorderedIsTrue, Value trueValue, Value falseValue);
 399 
 400     @Override
 401     public abstract Variable emitIntegerTestMove(Value leftVal, Value right, Value trueValue, Value falseValue);
 402 
 403     /**
 404      * Emits the single call operation at the heart of generating LIR for a
 405      * {@linkplain #emitForeignCall(ForeignCallLinkage, LIRFrameState, Value...) foreign call}.
 406      */
 407     protected abstract void emitForeignCallOp(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info);
 408 
 409     @Override
 410     public Variable emitForeignCall(ForeignCallLinkage linkage, LIRFrameState frameState, Value... args) {
 411         LIRFrameState state = null;
 412         if (linkage.needsDebugInfo()) {
 413             if (frameState != null) {
 414                 state = frameState;
 415             } else {
 416                 assert needOnlyOopMaps();
 417                 state = new LIRFrameState(null, null, null);
 418             }
 419         }
 420 
 421         // move the arguments into the correct location
 422         CallingConvention linkageCc = linkage.getOutgoingCallingConvention();
 423         res.getFrameMapBuilder().callsMethod(linkageCc);
 424         assert linkageCc.getArgumentCount() == args.length : "argument count mismatch";
 425         Value[] argLocations = new Value[args.length];
 426         for (int i = 0; i < args.length; i++) {
 427             Value arg = args[i];
 428             AllocatableValue loc = linkageCc.getArgument(i);
 429             emitMove(loc, arg);
 430             argLocations[i] = loc;
 431         }
 432         res.setForeignCall(true);
 433         emitForeignCallOp(linkage, linkageCc.getReturn(), argLocations, linkage.getTemporaries(), state);
 434 
 435         if (isLegal(linkageCc.getReturn())) {
 436             return emitMove(linkageCc.getReturn());
 437         } else {
 438             return null;
 439         }
 440     }
 441 
 442     @Override
 443     public void emitStrategySwitch(JavaConstant[] keyConstants, double[] keyProbabilities, LabelRef[] keyTargets, LabelRef defaultTarget, Variable value) {
 444         int keyCount = keyConstants.length;
 445         SwitchStrategy strategy = SwitchStrategy.getBestStrategy(keyProbabilities, keyConstants, keyTargets);
 446         long valueRange = keyConstants[keyCount - 1].asLong() - keyConstants[0].asLong() + 1;
 447         double tableSwitchDensity = keyCount / (double) valueRange;
 448         /*
 449          * This heuristic tries to find a compromise between the effort for the best switch strategy
 450          * and the density of a tableswitch. If the effort for the strategy is at least 4, then a
 451          * tableswitch is preferred if better than a certain value that starts at 0.5 and lowers
 452          * gradually with additional effort.
 453          */
 454         if (strategy.getAverageEffort() < 4 || tableSwitchDensity < (1 / Math.sqrt(strategy.getAverageEffort()))) {
 455             emitStrategySwitch(strategy, value, keyTargets, defaultTarget);
 456         } else {
 457             int minValue = keyConstants[0].asInt();
 458             assert valueRange < Integer.MAX_VALUE;
 459             LabelRef[] targets = new LabelRef[(int) valueRange];
 460             for (int i = 0; i < valueRange; i++) {
 461                 targets[i] = defaultTarget;
 462             }
 463             for (int i = 0; i < keyCount; i++) {
 464                 targets[keyConstants[i].asInt() - minValue] = keyTargets[i];
 465             }
 466             emitTableSwitch(minValue, defaultTarget, targets, value);
 467         }
 468     }
 469 
 470     @Override
 471     public abstract void emitStrategySwitch(SwitchStrategy strategy, Variable key, LabelRef[] keyTargets, LabelRef defaultTarget);
 472 
 473     protected abstract void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value key);
 474 
 475     @Override
 476     public void beforeRegisterAllocation() {
 477     }
 478 
 479     /**
 480      * Gets a garbage value for a given kind.
 481      */
 482     protected abstract JavaConstant zapValueForKind(PlatformKind kind);
 483 
 484     @Override
 485     public LIRKind getLIRKind(Stamp stamp) {
 486         return stamp.getLIRKind(lirKindTool);
 487     }
 488 
 489     protected LIRKind getAddressKind(Value base, long displacement, Value index) {
 490         if (LIRKind.isValue(base) && (index.equals(Value.ILLEGAL) || LIRKind.isValue(index))) {
 491             return LIRKind.value(target().arch.getWordKind());
 492         } else if (base.getValueKind() instanceof LIRKind && base.getValueKind(LIRKind.class).isReference(0) && displacement == 0L && index.equals(Value.ILLEGAL)) {
 493             return LIRKind.reference(target().arch.getWordKind());
 494         } else {
 495             return LIRKind.unknownReference(target().arch.getWordKind());
 496         }
 497     }
 498 
 499     @Override
 500     public AbstractBlockBase<?> getCurrentBlock() {
 501         return currentBlock;
 502     }
 503 
 504     @Override
 505     public LIRGenerationResult getResult() {
 506         return res;
 507     }
 508 
 509     @Override
 510     public void emitBlackhole(Value operand) {
 511         append(new StandardOp.BlackholeOp(operand));
 512     }
 513 
 514     @Override
 515     public LIRInstruction createBenchmarkCounter(String name, String group, Value increment) {
 516         throw GraalError.unimplemented();
 517     }
 518 
 519     @Override
 520     public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
 521         throw GraalError.unimplemented();
 522     }
 523 
 524     @Override
 525     public abstract SaveRegistersOp createZapRegisters(Register[] zappedRegisters, JavaConstant[] zapValues);
 526 
 527     @Override
 528     public SaveRegistersOp createZapRegisters() {
 529         Register[] zappedRegisters = getResult().getFrameMap().getRegisterConfig().getAllocatableRegisters().toArray();
 530         JavaConstant[] zapValues = new JavaConstant[zappedRegisters.length];
 531         for (int i = 0; i < zappedRegisters.length; i++) {
 532             PlatformKind kind = target().arch.getLargestStorableKind(zappedRegisters[i].getRegisterCategory());
 533             zapValues[i] = zapValueForKind(kind);
 534         }
 535         return createZapRegisters(zappedRegisters, zapValues);
 536     }
 537 
 538     @Override
 539     public abstract LIRInstruction createZapArgumentSpace(StackSlot[] zappedStack, JavaConstant[] zapValues);
 540 
 541     @Override
 542     public LIRInstruction zapArgumentSpace() {
 543         List<StackSlot> slots = null;
 544         for (AllocatableValue arg : res.getCallingConvention().getArguments()) {
 545             if (isStackSlot(arg)) {
 546                 if (slots == null) {
 547                     slots = new ArrayList<>();
 548                 }
 549                 slots.add((StackSlot) arg);
 550             } else {
 551                 assert !isVirtualStackSlot(arg);
 552             }
 553         }
 554         if (slots == null) {
 555             return null;
 556         }
 557         StackSlot[] zappedStack = slots.toArray(new StackSlot[slots.size()]);
 558         JavaConstant[] zapValues = new JavaConstant[zappedStack.length];
 559         for (int i = 0; i < zappedStack.length; i++) {
 560             PlatformKind kind = zappedStack[i].getPlatformKind();
 561             zapValues[i] = zapValueForKind(kind);
 562         }
 563         return createZapArgumentSpace(zappedStack, zapValues);
 564     }
 565 }