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