[vm] Faster double.floorToDouble/ceilToDouble/truncateToDouble/roundToDouble in AOT mode

This change replaces graph intrinsics and call specializer code for
certain _Double methods with a body created in FlowGraphBuilder.

double.floorToDouble micro-benchmark on AOT/x64:
Before BenchFloorToDouble(RunTime): 642.3240205523442 us.
After: BenchFloorToDouble(RunTime): 268.0320289466631 us.

double.floor micro-benchmark on AOT/x64:
Before BenchFloor(RunTime): 760.7630277672118 us.
After: BenchFloor(RunTime): 537.2132688691916 us.

TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/46650
Change-Id: I47f5d8a1bdc0f71965ad1763c7bc46540428c6cd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210652
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/compiler/asm_intrinsifier.h b/runtime/vm/compiler/asm_intrinsifier.h
index 4745bad..9d3f04b 100644
--- a/runtime/vm/compiler/asm_intrinsifier.h
+++ b/runtime/vm/compiler/asm_intrinsifier.h
@@ -31,9 +31,6 @@
  public:
   static intptr_t ParameterSlotFromSp();
 
-  static void IntrinsicCallPrologue(Assembler* assembler);
-  static void IntrinsicCallEpilogue(Assembler* assembler);
-
  private:
   friend class Intrinsifier;
 
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index e7f2ee1..454fb02 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -28,24 +28,6 @@
   return -1;
 }
 
-void AsmIntrinsifier::IntrinsicCallPrologue(Assembler* assembler) {
-  COMPILE_ASSERT(IsAbiPreservedRegister(CODE_REG));
-  COMPILE_ASSERT(IsAbiPreservedRegister(ARGS_DESC_REG));
-  COMPILE_ASSERT(IsAbiPreservedRegister(CALLEE_SAVED_TEMP));
-
-  // Save LR by moving it to a callee saved temporary register.
-  __ Comment("IntrinsicCallPrologue");
-  SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER(
-      __ mov(CALLEE_SAVED_TEMP, Operand(LR)));
-}
-
-void AsmIntrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
-  // Restore LR.
-  __ Comment("IntrinsicCallEpilogue");
-  RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR(
-      __ mov(LR, Operand(CALLEE_SAVED_TEMP)));
-}
-
 // Allocate a GrowableObjectArray:: using the backing array specified.
 // On stack: type argument (+1), data (+0).
 void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 1e0134a..03d3818 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -28,27 +28,6 @@
   return -1;
 }
 
-void AsmIntrinsifier::IntrinsicCallPrologue(Assembler* assembler) {
-  COMPILE_ASSERT(IsAbiPreservedRegister(CODE_REG));
-  COMPILE_ASSERT(!IsAbiPreservedRegister(ARGS_DESC_REG));
-  COMPILE_ASSERT(IsAbiPreservedRegister(CALLEE_SAVED_TEMP));
-  COMPILE_ASSERT(IsAbiPreservedRegister(CALLEE_SAVED_TEMP2));
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP != CODE_REG);
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP != ARGS_DESC_REG);
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP2 != CODE_REG);
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP2 != ARGS_DESC_REG);
-
-  __ Comment("IntrinsicCallPrologue");
-  SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER(__ mov(CALLEE_SAVED_TEMP, LR));
-  __ mov(CALLEE_SAVED_TEMP2, ARGS_DESC_REG);
-}
-
-void AsmIntrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
-  __ Comment("IntrinsicCallEpilogue");
-  RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR(__ mov(LR, CALLEE_SAVED_TEMP));
-  __ mov(ARGS_DESC_REG, CALLEE_SAVED_TEMP2);
-}
-
 // Allocate a GrowableObjectArray:: using the backing array specified.
 // On stack: type argument (+1), data (+0).
 void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index a6f1c9d..9c52f2a 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -35,18 +35,6 @@
   return 0;
 }
 
-void AsmIntrinsifier::IntrinsicCallPrologue(Assembler* assembler) {
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP != ARGS_DESC_REG);
-
-  assembler->Comment("IntrinsicCallPrologue");
-  assembler->movl(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
-}
-
-void AsmIntrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
-  assembler->Comment("IntrinsicCallEpilogue");
-  assembler->movl(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
-}
-
 // Allocate a GrowableObjectArray:: using the backing array specified.
 // On stack: type argument (+2), data (+1), return-address (+0).
 void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 3196c49..131a261 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -28,22 +28,6 @@
   return 0;
 }
 
-void AsmIntrinsifier::IntrinsicCallPrologue(Assembler* assembler) {
-  COMPILE_ASSERT(IsAbiPreservedRegister(CODE_REG));
-  COMPILE_ASSERT(!IsAbiPreservedRegister(ARGS_DESC_REG));
-  COMPILE_ASSERT(IsAbiPreservedRegister(CALLEE_SAVED_TEMP));
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP != CODE_REG);
-  COMPILE_ASSERT(CALLEE_SAVED_TEMP != ARGS_DESC_REG);
-
-  assembler->Comment("IntrinsicCallPrologue");
-  assembler->movq(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
-}
-
-void AsmIntrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
-  assembler->Comment("IntrinsicCallEpilogue");
-  assembler->movq(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
-}
-
 // Allocate a GrowableObjectArray using the backing array specified.
 // On stack: type argument (+2), data (+1), return-address (+0).
 void AsmIntrinsifier::GrowableArray_Allocate(Assembler* assembler,
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 28f499c..c7973a2 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -6178,10 +6178,7 @@
   switch (kind) {
     case MethodRecognizer::kDoubleTruncate:
     case MethodRecognizer::kDoubleFloor:
-    case MethodRecognizer::kDoubleCeil: {
-      ASSERT(!TargetCPUFeatures::double_truncate_round_supported());
-      return 1;
-    }
+    case MethodRecognizer::kDoubleCeil:
     case MethodRecognizer::kDoubleRound:
     case MethodRecognizer::kMathAtan:
     case MethodRecognizer::kMathTan:
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 911426b..d8b607c 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -8394,6 +8394,11 @@
     return kUnboxedDouble;
   }
 
+  virtual SpeculativeMode SpeculativeModeOfInput(intptr_t idx) const {
+    ASSERT(idx == 0);
+    return kNotSpeculative;
+  }
+
   virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); }
 
   virtual bool AttributesEqual(const Instruction& other) const {
@@ -8512,6 +8517,11 @@
     return kUnboxedDouble;
   }
 
+  virtual SpeculativeMode SpeculativeModeOfInput(intptr_t idx) const {
+    ASSERT((0 <= idx) && (idx < InputCount()));
+    return kNotSpeculative;
+  }
+
   virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); }
 
   virtual intptr_t InputCount() const { return inputs_->length(); }
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 944673b..c87f165 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -5344,30 +5344,36 @@
                                                                bool opt) const {
   // Calling convention on x64 uses XMM0 and XMM1 to pass the first two
   // double arguments and XMM0 to return the result.
-  //
-  // TODO(sjindel): allow XMM0 to be used. Requires refactoring InvokeDoublePow
-  // to allow input 1/output register to be equal.
-  ASSERT((InputCount() == 1) || (InputCount() == 2));
-  const intptr_t kNumTemps =
-      (recognized_kind() == MethodRecognizer::kMathDoublePow) ? 4 : 1;
-  LocationSummary* result = new (zone)
-      LocationSummary(zone, InputCount(), kNumTemps, LocationSummary::kCall);
   ASSERT(R13 != CALLEE_SAVED_TEMP);
   ASSERT(((1 << R13) & CallingConventions::kCalleeSaveCpuRegisters) != 0);
-  result->set_temp(0, Location::RegisterLocation(R13));
-  result->set_in(0, Location::FpuRegisterLocation(XMM2));
-  if (InputCount() == 2) {
-    result->set_in(1, Location::FpuRegisterLocation(XMM1));
-  }
+
   if (recognized_kind() == MethodRecognizer::kMathDoublePow) {
+    ASSERT(InputCount() == 2);
+    const intptr_t kNumTemps = 4;
+    LocationSummary* result = new (zone)
+        LocationSummary(zone, InputCount(), kNumTemps, LocationSummary::kCall);
+    result->set_in(0, Location::FpuRegisterLocation(XMM2));
+    result->set_in(1, Location::FpuRegisterLocation(XMM1));
+    result->set_temp(0, Location::RegisterLocation(R13));
     // Temp index 1.
     result->set_temp(1, Location::RegisterLocation(RAX));
     // Temp index 2.
     result->set_temp(2, Location::FpuRegisterLocation(XMM4));
     // Block XMM0 for the calling convention.
     result->set_temp(3, Location::FpuRegisterLocation(XMM0));
+    result->set_out(0, Location::FpuRegisterLocation(XMM3));
+    return result;
   }
-  result->set_out(0, Location::FpuRegisterLocation(XMM3));
+  ASSERT((InputCount() == 1) || (InputCount() == 2));
+  const intptr_t kNumTemps = 1;
+  LocationSummary* result = new (zone)
+      LocationSummary(zone, InputCount(), kNumTemps, LocationSummary::kCall);
+  result->set_temp(0, Location::RegisterLocation(R13));
+  result->set_in(0, Location::FpuRegisterLocation(XMM0));
+  if (InputCount() == 2) {
+    result->set_in(1, Location::FpuRegisterLocation(XMM1));
+  }
+  result->set_out(0, Location::FpuRegisterLocation(XMM0));
   return result;
 }
 
@@ -5509,17 +5515,20 @@
     InvokeDoublePow(compiler, this);
     return;
   }
-  // Save RSP.
-  __ movq(locs()->temp(kSavedSpTempIndex).reg(), RSP);
-  __ ReserveAlignedFrameSpace(0);
-  __ movaps(XMM0, locs()->in(0).fpu_reg());
+
+  ASSERT(locs()->in(0).fpu_reg() == XMM0);
   if (InputCount() == 2) {
     ASSERT(locs()->in(1).fpu_reg() == XMM1);
   }
 
+  // Save RSP.
+  __ movq(locs()->temp(kSavedSpTempIndex).reg(), RSP);
+  __ ReserveAlignedFrameSpace(0);
+
   ASSERT(TargetFunction().is_leaf());  // No deopt info needed.
   __ CallRuntime(TargetFunction(), InputCount());
-  __ movaps(locs()->out(0).fpu_reg(), XMM0);
+  ASSERT(locs()->out(0).fpu_reg() == XMM0);
+
   // Restore RSP.
   __ movq(RSP, locs()->temp(kSavedSpTempIndex).reg());
 }
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index d386935..61e2f54 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -968,21 +968,6 @@
   return TryInlineImplicitInstanceGetter(call);
 }
 
-void CallSpecializer::ReplaceWithMathCFunction(
-    InstanceCallInstr* call,
-    MethodRecognizer::Kind recognized_kind) {
-  ASSERT(call->type_args_len() == 0);
-  AddReceiverCheck(call);
-  ZoneGrowableArray<Value*>* args =
-      new (Z) ZoneGrowableArray<Value*>(call->ArgumentCount());
-  for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
-    args->Add(new (Z) Value(call->ArgumentAt(i)));
-  }
-  InvokeMathCFunctionInstr* invoke = new (Z) InvokeMathCFunctionInstr(
-      args, call->deopt_id(), recognized_kind, call->source());
-  ReplaceCall(call, invoke);
-}
-
 // Inline only simple, frequently called core library methods.
 bool CallSpecializer::TryInlineInstanceMethod(InstanceCallInstr* call) {
   const CallTargets& targets = call->Targets();
@@ -1034,23 +1019,6 @@
         ReplaceCall(call, d2i_instr);
         return true;
       }
-      case MethodRecognizer::kDoubleMod:
-      case MethodRecognizer::kDoubleRound:
-        ReplaceWithMathCFunction(call, recognized_kind);
-        return true;
-      case MethodRecognizer::kDoubleTruncate:
-      case MethodRecognizer::kDoubleFloor:
-      case MethodRecognizer::kDoubleCeil:
-        if (!TargetCPUFeatures::double_truncate_round_supported()) {
-          ReplaceWithMathCFunction(call, recognized_kind);
-        } else {
-          AddReceiverCheck(call);
-          DoubleToDoubleInstr* d2d_instr =
-              new (Z) DoubleToDoubleInstr(new (Z) Value(call->ArgumentAt(0)),
-                                          recognized_kind, call->deopt_id());
-          ReplaceCall(call, d2d_instr);
-        }
-        return true;
       default:
         break;
     }
diff --git a/runtime/vm/compiler/call_specializer.h b/runtime/vm/compiler/call_specializer.h
index e82a69c..7e84402 100644
--- a/runtime/vm/compiler/call_specializer.h
+++ b/runtime/vm/compiler/call_specializer.h
@@ -172,9 +172,6 @@
   bool TryOptimizeInstanceOfUsingStaticTypes(InstanceCallInstr* call,
                                              const AbstractType& type);
 
-  void ReplaceWithMathCFunction(InstanceCallInstr* call,
-                                MethodRecognizer::Kind recognized_kind);
-
   bool TryStringLengthOneEquality(InstanceCallInstr* call, Token::Kind op_kind);
 
   void SpecializePolymorphicInstanceCall(PolymorphicInstanceCallInstr* call);
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index 4c3a5cf..4e16bfe 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -1193,5 +1193,25 @@
   return instructions;
 }
 
+Fragment BaseFlowGraphBuilder::InvokeMathCFunction(
+    MethodRecognizer::Kind recognized_kind,
+    intptr_t num_inputs) {
+  InputsArray* args = GetArguments(num_inputs);
+  auto* instr = new (Z)
+      InvokeMathCFunctionInstr(args, GetNextDeoptId(), recognized_kind,
+                               InstructionSource(TokenPosition::kNoSource));
+  Push(instr);
+  return Fragment(instr);
+}
+
+Fragment BaseFlowGraphBuilder::DoubleToDouble(
+    MethodRecognizer::Kind recognized_kind) {
+  Value* value = Pop();
+  auto* instr =
+      new (Z) DoubleToDoubleInstr(value, recognized_kind, GetNextDeoptId());
+  Push(instr);
+  return Fragment(instr);
+}
+
 }  // namespace kernel
 }  // namespace dart
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index edad1e4..f3ef8e0 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -436,6 +436,14 @@
   // Sets raw parameter variables to inferred constant values.
   Fragment InitConstantParameters();
 
+  Fragment InvokeMathCFunction(MethodRecognizer::Kind recognized_kind,
+                               intptr_t num_inputs);
+
+  // Pops double value and converts it to double as specified
+  // by the recognized method (kDoubleTruncate,
+  // kDoubleFloor or kDoubleCeil).
+  Fragment DoubleToDouble(MethodRecognizer::Kind recognized_kind);
+
   // Returns whether this function has a saved arguments descriptor array.
   bool has_saved_args_desc_array() {
     return function_.HasSavedArgumentsDescriptor();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 2054bbb..870c86f 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -988,7 +988,7 @@
     case UntaggedFunction::kSetterFunction:
     case UntaggedFunction::kClosureFunction:
     case UntaggedFunction::kConstructor: {
-      if (B->IsRecognizedMethodForFlowGraph(function)) {
+      if (FlowGraphBuilder::IsRecognizedMethodForFlowGraph(function)) {
         return B->BuildGraphOfRecognizedMethod(function);
       }
       return BuildGraphOfFunction(function.IsGenerativeConstructor());
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 0930d3d..2e2f061 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -8,6 +8,7 @@
 #include "platform/globals.h"
 #include "vm/class_id.h"
 #include "vm/compiler/aot/precompiler.h"
+#include "vm/compiler/backend/flow_graph_compiler.h"
 #include "vm/compiler/backend/il.h"
 #include "vm/compiler/backend/il_printer.h"
 #include "vm/compiler/backend/locations.h"
@@ -891,6 +892,12 @@
     case MethodRecognizer::kUtf8DecoderScan:
     case MethodRecognizer::kHas63BitSmis:
       return true;
+    case MethodRecognizer::kDoubleMod:
+    case MethodRecognizer::kDoubleRound:
+    case MethodRecognizer::kDoubleTruncate:
+    case MethodRecognizer::kDoubleFloor:
+    case MethodRecognizer::kDoubleCeil:
+      return FlowGraphCompiler::SupportsUnboxedDoubles();
     default:
       return false;
   }
@@ -1506,6 +1513,24 @@
       body += LoadIndexed(kIntPtrCid);
       body += Box(kUnboxedIntPtr);
     } break;
+    case MethodRecognizer::kDoubleMod:
+    case MethodRecognizer::kDoubleRound:
+    case MethodRecognizer::kDoubleTruncate:
+    case MethodRecognizer::kDoubleFloor:
+    case MethodRecognizer::kDoubleCeil: {
+      for (intptr_t i = 0, n = function.NumParameters(); i < n; ++i) {
+        body += LoadLocal(parsed_function_->RawParameterVariable(i));
+      }
+      if (!CompilerState::Current().is_aot() &&
+          TargetCPUFeatures::double_truncate_round_supported() &&
+          ((kind == MethodRecognizer::kDoubleTruncate) ||
+           (kind == MethodRecognizer::kDoubleFloor) ||
+           (kind == MethodRecognizer::kDoubleCeil))) {
+        body += DoubleToDouble(kind);
+      } else {
+        body += InvokeMathCFunction(kind, function.NumParameters());
+      }
+    } break;
     default: {
       UNREACHABLE();
       break;
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index f9d1a6e..20c5b77c 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -65,6 +65,10 @@
 
   FlowGraph* BuildGraph();
 
+  // Returns true if given [function] is recognized for flow
+  // graph building and its body is expressed in a custom-built IL.
+  static bool IsRecognizedMethodForFlowGraph(const Function& function);
+
  private:
   BlockEntryInstr* BuildPrologue(BlockEntryInstr* normal_entry,
                                  PrologueInfo* prologue_info);
@@ -145,8 +149,6 @@
   Fragment NativeFunctionBody(const Function& function,
                               LocalVariable* first_parameter);
 
-  // Every recognized method has a body expressed in IL.
-  bool IsRecognizedMethodForFlowGraph(const Function& function);
   FlowGraph* BuildGraphOfRecognizedMethod(const Function& function);
 
   Fragment BuildTypedDataViewFactoryConstructor(const Function& function,
diff --git a/runtime/vm/compiler/graph_intrinsifier.cc b/runtime/vm/compiler/graph_intrinsifier.cc
index 49287d4..a97c2fb 100644
--- a/runtime/vm/compiler/graph_intrinsifier.cc
+++ b/runtime/vm/compiler/graph_intrinsifier.cc
@@ -1121,40 +1121,6 @@
   return BuildInvokeMathCFunction(flow_graph, MethodRecognizer::kMathLog);
 }
 
-bool GraphIntrinsifier::Build_DoubleMod(FlowGraph* flow_graph) {
-  return BuildInvokeMathCFunction(flow_graph, MethodRecognizer::kDoubleMod,
-                                  /* num_parameters = */ 2);
-}
-
-bool GraphIntrinsifier::Build_DoubleCeil(FlowGraph* flow_graph) {
-  // TODO(johnmccutchan): On X86 this intrinsic can be written in a different
-  // way.
-  if (TargetCPUFeatures::double_truncate_round_supported()) return false;
-
-  return BuildInvokeMathCFunction(flow_graph, MethodRecognizer::kDoubleCeil);
-}
-
-bool GraphIntrinsifier::Build_DoubleFloor(FlowGraph* flow_graph) {
-  // TODO(johnmccutchan): On X86 this intrinsic can be written in a different
-  // way.
-  if (TargetCPUFeatures::double_truncate_round_supported()) return false;
-
-  return BuildInvokeMathCFunction(flow_graph, MethodRecognizer::kDoubleFloor);
-}
-
-bool GraphIntrinsifier::Build_DoubleTruncate(FlowGraph* flow_graph) {
-  // TODO(johnmccutchan): On X86 this intrinsic can be written in a different
-  // way.
-  if (TargetCPUFeatures::double_truncate_round_supported()) return false;
-
-  return BuildInvokeMathCFunction(flow_graph,
-                                  MethodRecognizer::kDoubleTruncate);
-}
-
-bool GraphIntrinsifier::Build_DoubleRound(FlowGraph* flow_graph) {
-  return BuildInvokeMathCFunction(flow_graph, MethodRecognizer::kDoubleRound);
-}
-
 bool GraphIntrinsifier::Build_ImplicitGetter(FlowGraph* flow_graph) {
   // This code will only be invoked if our assumptions have been met (see
   // [Intrinsifier::CanIntrinsifyFieldAccessor])
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 9680f0b..8bf5cf9 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -87,6 +87,11 @@
   V(_Double, _sub, DoubleSub, 0x28474c2e)                                      \
   V(_Double, _mul, DoubleMul, 0x1f98c76c)                                      \
   V(_Double, _div, DoubleDiv, 0x287d3791)                                      \
+  V(_Double, _modulo, DoubleMod, 0xfdb397ef)                                   \
+  V(_Double, ceilToDouble, DoubleCeil, 0x5f1bced9)                             \
+  V(_Double, floorToDouble, DoubleFloor, 0x54b4cb48)                           \
+  V(_Double, roundToDouble, DoubleRound, 0x5649ca00)                           \
+  V(_Double, truncateToDouble, DoubleTruncate, 0x62d48659)                     \
   V(::, min, MathMin, 0x504a28df)                                              \
   V(::, max, MathMax, 0xead7161a)                                              \
   V(::, _doublePow, MathDoublePow, 0x989f3334)                                 \
@@ -366,11 +371,6 @@
   V(_IntegerImplementation, >>, Integer_sar, 0x4a3615a7)                       \
   V(_IntegerImplementation, >>>, Integer_shr, 0x2bac5209)                      \
   V(_Double, unary-, DoubleFlipSignBit, 0x3d39082b)                            \
-  V(_Double, truncateToDouble, DoubleTruncate, 0x62d48298)                     \
-  V(_Double, roundToDouble, DoubleRound, 0x5649c63f)                           \
-  V(_Double, floorToDouble, DoubleFloor, 0x54b4c787)                           \
-  V(_Double, ceilToDouble, DoubleCeil, 0x5f1bcb18)                             \
-  V(_Double, _modulo, DoubleMod, 0xfdb3942e)
 
 #define GRAPH_INTRINSICS_LIST(V)                                               \
   GRAPH_CORE_INTRINSICS_LIST(V)                                                \
diff --git a/sdk/lib/_internal/vm/lib/double.dart b/sdk/lib/_internal/vm/lib/double.dart
index ab3b88d..e1dca3d 100644
--- a/sdk/lib/_internal/vm/lib/double.dart
+++ b/sdk/lib/_internal/vm/lib/double.dart
@@ -67,7 +67,8 @@
     return _modulo(other.toDouble());
   }
 
-  @pragma("vm:recognized", "graph-intrinsic")
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
   @pragma("vm:exact-result-type", _Double)
   double _modulo(double other) native "Double_modulo";
 
@@ -181,16 +182,20 @@
   int ceil() => ceilToDouble().toInt();
   int truncate() => truncateToDouble().toInt();
 
-  @pragma("vm:recognized", "graph-intrinsic")
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
   @pragma("vm:exact-result-type", _Double)
   double roundToDouble() native "Double_round";
-  @pragma("vm:recognized", "graph-intrinsic")
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
   @pragma("vm:exact-result-type", _Double)
   double floorToDouble() native "Double_floor";
-  @pragma("vm:recognized", "graph-intrinsic")
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
   @pragma("vm:exact-result-type", _Double)
   double ceilToDouble() native "Double_ceil";
-  @pragma("vm:recognized", "graph-intrinsic")
+  @pragma("vm:recognized", "other")
+  @pragma("vm:prefer-inline")
   @pragma("vm:exact-result-type", _Double)
   double truncateToDouble() native "Double_truncate";