1 /*
   2  * Copyright (c) 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.replacements.amd64;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1;
  26 
  27 import org.graalvm.compiler.core.common.type.FloatStamp;
  28 import org.graalvm.compiler.core.common.type.Stamp;
  29 import org.graalvm.compiler.debug.GraalError;
  30 import org.graalvm.compiler.graph.NodeClass;
  31 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  32 import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool;
  33 import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMode;
  34 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
  35 import org.graalvm.compiler.nodeinfo.NodeInfo;
  36 import org.graalvm.compiler.nodes.ConstantNode;
  37 import org.graalvm.compiler.nodes.ValueNode;
  38 import org.graalvm.compiler.nodes.calc.UnaryNode;
  39 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
  40 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  41 
  42 import jdk.vm.ci.meta.JavaConstant;
  43 import jdk.vm.ci.meta.JavaKind;
  44 
  45 /**
  46  * Round floating-point value.
  47  */
  48 @NodeInfo(cycles = CYCLES_1)
  49 public final class AMD64RoundNode extends UnaryNode implements ArithmeticLIRLowerable {
  50     public static final NodeClass<AMD64RoundNode> TYPE = NodeClass.create(AMD64RoundNode.class);
  51 
  52     private final RoundingMode mode;
  53 
  54     public AMD64RoundNode(ValueNode value, RoundingMode mode) {
  55         super(TYPE, roundStamp((FloatStamp) value.stamp(), mode), value);
  56         this.mode = mode;
  57     }
  58 
  59     private static double round(RoundingMode mode, double input) {
  60         switch (mode) {
  61             case DOWN:
  62                 return Math.floor(input);
  63             case NEAREST:
  64                 return Math.rint(input);
  65             case UP:
  66                 return Math.ceil(input);
  67             case TRUNCATE:
  68                 return (long) input;
  69             default:
  70                 throw GraalError.unimplemented("unimplemented RoundingMode " + mode);
  71         }
  72     }
  73 
  74     private static FloatStamp roundStamp(FloatStamp stamp, RoundingMode mode) {
  75         double min = stamp.lowerBound();
  76         min = Math.min(min, round(mode, min));
  77 
  78         double max = stamp.upperBound();
  79         max = Math.max(max, round(mode, max));
  80 
  81         return new FloatStamp(stamp.getBits(), min, max, stamp.isNonNaN());
  82     }
  83 
  84     @Override
  85     public Stamp foldStamp(Stamp newStamp) {
  86         assert newStamp.isCompatible(getValue().stamp());
  87         return roundStamp((FloatStamp) newStamp, mode);
  88     }
  89 
  90     public ValueNode tryFold(ValueNode input) {
  91         if (input.isConstant()) {
  92             JavaConstant c = input.asJavaConstant();
  93             if (c.getJavaKind() == JavaKind.Double) {
  94                 return ConstantNode.forDouble(round(mode, c.asDouble()));
  95             } else if (c.getJavaKind() == JavaKind.Float) {
  96                 return ConstantNode.forFloat((float) round(mode, c.asFloat()));
  97             }
  98         }
  99         return null;
 100     }
 101 
 102     @Override
 103     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
 104         ValueNode folded = tryFold(forValue);
 105         return folded != null ? folded : this;
 106     }
 107 
 108     @Override
 109     public void generate(NodeLIRBuilderTool builder, ArithmeticLIRGeneratorTool gen) {
 110         builder.setResult(this, ((AMD64ArithmeticLIRGeneratorTool) gen).emitRound(builder.operand(getValue()), mode));
 111     }
 112 }