[vm] Add `LoadThreadInstr`

Introduces a new IL instruction to load the THR register as an untagged
value in IL.

Split off from https://dart-review.googlesource.com/c/sdk/+/229544.

TEST=runtime/vm/compiler/backend/il_test.cc vm/cc/IRTest_LoadThread

Bug: https://github.com/dart-lang/sdk/issues/47777

Change-Id: Ic6bf59b05c89593773dbc91d623cd0078657c8ab
Cq-Include-Trybots: luci.dart.try:vm-kernel-win-debug-x64-try,vm-kernel-linux-debug-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/235602
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index eed551a..9cf2ade 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -1454,6 +1454,10 @@
   SetValue(instr, non_constant_);
 }
 
+void ConstantPropagator::VisitLoadThread(LoadThreadInstr* instr) {
+  SetValue(instr, non_constant_);
+}
+
 void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) {
   // TODO(kmillikin): Handle unary operations.
   SetValue(instr, non_constant_);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 5a742d0..e69dc63 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -13,6 +13,7 @@
 #include "vm/compiler/backend/flow_graph_compiler.h"
 #include "vm/compiler/backend/linearscan.h"
 #include "vm/compiler/backend/locations.h"
+#include "vm/compiler/backend/locations_helpers.h"
 #include "vm/compiler/backend/loops.h"
 #include "vm/compiler/backend/range_analysis.h"
 #include "vm/compiler/ffi/frame_rebase.h"
@@ -6863,6 +6864,11 @@
   return marshaller_.RepInFfiCall(compiler::ffi::kResultIndex);
 }
 
+// TODO(http://dartbug.com/48543): integrate with register allocator directly.
+DEFINE_BACKEND(LoadThread, (Register out)) {
+  __ MoveRegister(out, THR);
+}
+
 // SIMD
 
 SimdOpInstr::Kind SimdOpInstr::KindForOperator(MethodRecognizer::Kind kind) {
@@ -7038,12 +7044,10 @@
 
 // Define the metadata array.
 static const SimdOpInfo simd_op_information[] = {
-#define PP_APPLY(M, Args) M Args
 #define CASE(Arity, Mask, Name, Args, Result)                                  \
   {Arity, HAS_##Mask, REP(Result), {PP_APPLY(ENCODE_INPUTS_##Arity, Args)}},
     SIMD_OP_LIST(CASE, CASE)
 #undef CASE
-#undef PP_APPLY
 };
 
 // Undef all auxiliary macros.
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index d8d0c57..6e916cd 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -512,6 +512,7 @@
   M(BoxSmallInt, kNoGC)                                                        \
   M(IntConverter, kNoGC)                                                       \
   M(BitCast, kNoGC)                                                            \
+  M(LoadThread, kNoGC)                                                         \
   M(Deoptimize, kNoGC)                                                         \
   M(SimdOp, kNoGC)
 
@@ -9279,6 +9280,32 @@
   DISALLOW_COPY_AND_ASSIGN(BitCastInstr);
 };
 
+class LoadThreadInstr : public TemplateDefinition<0, NoThrow, Pure> {
+ public:
+  LoadThreadInstr() : TemplateDefinition(DeoptId::kNone) {}
+
+  virtual bool ComputeCanDeoptimize() const { return false; }
+
+  virtual Representation representation() const { return kUntagged; }
+
+  virtual Representation RequiredInputRepresentation(intptr_t idx) const {
+    UNREACHABLE();
+  }
+
+  virtual CompileType ComputeType() const { return CompileType::Int(); }
+
+  // CSE is allowed. The thread should always be the same value.
+  virtual bool AttributesEqual(const Instruction& other) const {
+    ASSERT(other.IsLoadThread());
+    return true;
+  }
+
+  DECLARE_INSTRUCTION(LoadThread);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadThreadInstr);
+};
+
 // SimdOpInstr
 //
 // All SIMD intrinsics and recognized methods are represented via instances
diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc
index 0ce40bd..65dadc0 100644
--- a/runtime/vm/compiler/backend/il_test.cc
+++ b/runtime/vm/compiler/backend/il_test.cc
@@ -872,4 +872,84 @@
   EXPECT_EQ(100, ptr2);
 }
 
+ISOLATE_UNIT_TEST_CASE(IRTest_LoadThread) {
+  // clang-format off
+  auto kScript = R"(
+    import 'dart:ffi';
+
+    int myFunction() {
+      return 100;
+    }
+
+    void anotherFunction() {}
+  )";
+  // clang-format on
+
+  const auto& root_library = Library::Handle(LoadTestScript(kScript));
+  Zone* const zone = Thread::Current()->zone();
+  auto& invoke_result = Instance::Handle(zone);
+  invoke_result ^= Invoke(root_library, "myFunction");
+  EXPECT_EQ(Smi::New(100), invoke_result.ptr());
+
+  const auto& my_function =
+      Function::Handle(GetFunction(root_library, "myFunction"));
+
+  TestPipeline pipeline(my_function, CompilerPass::kJIT);
+  FlowGraph* flow_graph = pipeline.RunPasses({
+      CompilerPass::kComputeSSA,
+  });
+
+  ReturnInstr* return_instr = nullptr;
+  {
+    ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
+
+    EXPECT(cursor.TryMatch({
+        kMoveGlob,
+        {kMatchReturn, &return_instr},
+    }));
+  }
+
+  auto* const load_thread_instr = new (zone) LoadThreadInstr();
+  flow_graph->InsertBefore(return_instr, load_thread_instr, nullptr,
+                           FlowGraph::kValue);
+  auto load_thread_value = Value(load_thread_instr);
+
+  auto* const convert_instr = new (zone) IntConverterInstr(
+      kUntagged, kUnboxedFfiIntPtr, &load_thread_value, DeoptId::kNone);
+  flow_graph->InsertBefore(return_instr, convert_instr, nullptr,
+                           FlowGraph::kValue);
+  auto convert_value = Value(convert_instr);
+
+  auto* const box_instr = BoxInstr::Create(kUnboxedFfiIntPtr, &convert_value);
+  flow_graph->InsertBefore(return_instr, box_instr, nullptr, FlowGraph::kValue);
+
+  return_instr->InputAt(0)->definition()->ReplaceUsesWith(box_instr);
+
+  {
+    // Check we constructed the right graph.
+    ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
+    EXPECT(cursor.TryMatch({
+        kMoveGlob,
+        kMatchAndMoveLoadThread,
+        kMatchAndMoveIntConverter,
+        kMatchAndMoveBox,
+        kMatchReturn,
+    }));
+  }
+
+  pipeline.RunForcedOptimizedAfterSSAPasses();
+
+  {
+#if !defined(PRODUCT)
+    SetFlagScope<bool> sfs(&FLAG_disassemble_optimized, true);
+#endif
+    pipeline.CompileGraphAndAttachFunction();
+  }
+
+  // Ensure we can successfully invoke the function.
+  invoke_result ^= Invoke(root_library, "myFunction");
+  intptr_t result_int = Integer::Cast(invoke_result).AsInt64Value();
+  EXPECT_EQ(reinterpret_cast<intptr_t>(thread), result_int);
+}
+
 }  // namespace dart