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