[llvm] dce25be - [WebAssembly][GlobalISel] Implement `COPY` (#197256)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 30 00:33:17 PDT 2026


Author: Demetrius Kanios
Date: 2026-06-30T00:33:12-07:00
New Revision: dce25be9ad415ec18b160db3379490cb2bd260e8

URL: https://github.com/llvm/llvm-project/commit/dce25be9ad415ec18b160db3379490cb2bd260e8
DIFF: https://github.com/llvm/llvm-project/commit/dce25be9ad415ec18b160db3379490cb2bd260e8.diff

LOG: [WebAssembly][GlobalISel] Implement `COPY` (#197256)

Adds instruction select handling and tests for `COPY`. While `COPY` gets
dissolved in the end, these changes are required to keep the selector
from crashing when trying to assign a regclass to its operands.

Split from #157161

Added: 
    llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/copy.mir

Modified: 
    llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 5c27dd9b5b201..2331b8b636603 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -46,6 +46,7 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
 
 private:
   bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
+  bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
 
   const WebAssemblyTargetMachine &TM;
   // const WebAssemblySubtarget &STI;
@@ -83,13 +84,68 @@ WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
 {
 }
 
+bool WebAssemblyInstructionSelector::selectCopy(
+    MachineInstr &I, MachineRegisterInfo &MRI) const {
+  const TargetRegisterClass *DstRC =
+      TRI.getConstrainedRegClassForOperand(I.getOperand(0), MRI);
+  if (!DstRC)
+    return false;
+
+  const TargetRegisterClass *SrcRC =
+      TRI.getConstrainedRegClassForOperand(I.getOperand(1), MRI);
+  if (!SrcRC)
+    return false;
+
+  Register DstReg = I.getOperand(0).getReg();
+  Register SrcReg = I.getOperand(1).getReg();
+
+  if (DstReg.isVirtual())
+    RBI.constrainGenericRegister(DstReg, *DstRC, MRI);
+  if (SrcReg.isVirtual())
+    RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI);
+
+  if (DstRC != SrcRC) {
+    if (DstReg.isPhysical() || SrcReg.isPhysical())
+      llvm_unreachable("COPY to/from SP[32/64] or FP[32/64] with mismatching "
+                       "classes not currently supported");
+
+    if (DstRC == &WebAssembly::I32RegClass &&
+        SrcRC == &WebAssembly::F32RegClass) {
+      I.setDesc(TII.get(WebAssembly::I32_REINTERPRET_F32));
+      return true;
+    }
+    if (DstRC == &WebAssembly::F32RegClass &&
+        SrcRC == &WebAssembly::I32RegClass) {
+      I.setDesc(TII.get(WebAssembly::F32_REINTERPRET_I32));
+      return true;
+    }
+    if (DstRC == &WebAssembly::I64RegClass &&
+        SrcRC == &WebAssembly::F64RegClass) {
+      I.setDesc(TII.get(WebAssembly::I64_REINTERPRET_F64));
+      return true;
+    }
+    if (DstRC == &WebAssembly::F64RegClass &&
+        SrcRC == &WebAssembly::I64RegClass) {
+      I.setDesc(TII.get(WebAssembly::F64_REINTERPRET_I64));
+      return true;
+    }
+
+    llvm_unreachable("COPY between unsupported reg classes.");
+  }
+
+  return true;
+}
+
 bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
   MachineBasicBlock &MBB = *I.getParent();
   MachineFunction &MF = *MBB.getParent();
   MachineRegisterInfo &MRI = MF.getRegInfo();
 
-  if (!I.isPreISelOpcode())
+  if (!I.isPreISelOpcode()) {
+    if (I.isCopy())
+      return selectCopy(I, MRI);
     return true;
+  }
 
   if (selectImpl(I, *CoverageInfo))
     return true;

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
index 4b4f07bd782ad..0c8241461f805 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
@@ -184,8 +184,23 @@ WebAssemblyRegisterInfo::getConstrainedRegClassForOperand(
     const MachineOperand &MO, const MachineRegisterInfo &MRI) const {
   assert(MO.isReg());
 
-  const RegClassOrRegBank &RegClassOrBank =
-      MRI.getRegClassOrRegBank(MO.getReg());
+  Register Reg = MO.getReg();
+
+  if (Reg.isPhysical()) {
+    switch (Reg.id()) {
+    case WebAssembly::SP32:
+    case WebAssembly::FP32:
+      return &WebAssembly::I32RegClass;
+    case WebAssembly::SP64:
+    case WebAssembly::FP64:
+      return &WebAssembly::I64RegClass;
+      break;
+    default:
+      return nullptr;
+    }
+  }
+
+  const RegClassOrRegBank &RegClassOrBank = MRI.getRegClassOrRegBank(Reg);
 
   if (RegClassOrBank.isNull())
     return nullptr;

diff  --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/copy.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/copy.mir
new file mode 100644
index 0000000000000..a692af7437071
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/copy.mir
@@ -0,0 +1,294 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass=regbankselect,instruction-select %s -o - | FileCheck %s
+
+---
+name: copy_i32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: RETURN [[ARGUMENT_i32_]], implicit-def $arguments
+    %0:i32(i32) = ARGUMENT_i32 0, implicit $arguments
+    %1:_(i32) = COPY %0(i32)
+    RETURN %1(i32), implicit-def $arguments
+...
+
+---
+name: copy_i64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: RETURN [[ARGUMENT_i64_]], implicit-def $arguments
+    %0:i64(i64) = ARGUMENT_i64 0, implicit $arguments
+    %1:_(i64) = COPY %0(i64)
+    RETURN %1(i64), implicit-def $arguments
+...
+
+---
+name: copy_f32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_f32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32 = ARGUMENT_f32 0, implicit $arguments
+    ; CHECK-NEXT: RETURN [[ARGUMENT_f32_]], implicit-def $arguments
+    %0:f32(f32) = ARGUMENT_f32 0, implicit $arguments
+    %1:_(f32) = COPY %0(f32)
+    RETURN %1(f32), implicit-def $arguments
+...
+
+---
+name: copy_f64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_f64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_f64_:%[0-9]+]]:f64 = ARGUMENT_f64 0, implicit $arguments
+    ; CHECK-NEXT: RETURN [[ARGUMENT_f64_]], implicit-def $arguments
+    %0:f64(f64) = ARGUMENT_f64 0, implicit $arguments
+    %1:_(f64) = COPY %0(f64)
+    RETURN %1(f64), implicit-def $arguments
+...
+
+---
+name: copy_to_sp32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_to_sp32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: $sp32 = COPY [[ARGUMENT_i32_]]
+    ; CHECK-NEXT: RETURN implicit-def $arguments
+    %0:i32(i32) = ARGUMENT_i32 0, implicit $arguments
+    $sp32 = COPY %0(i32)
+    RETURN implicit-def $arguments
+...
+
+---
+name: copy_to_sp64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_to_sp64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: $sp64 = COPY [[ARGUMENT_i64_]]
+    ; CHECK-NEXT: RETURN implicit-def $arguments
+    %0:i64(i64) = ARGUMENT_i64 0, implicit $arguments
+    $sp64 = COPY %0(i64)
+    RETURN implicit-def $arguments
+...
+
+---
+name: copy_from_sp32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_from_sp32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i32 = COPY $sp32
+    ; CHECK-NEXT: RETURN [[COPY]], implicit-def $arguments
+    %0:_(i32) = COPY $sp32
+    RETURN %0(i32), implicit-def $arguments
+...
+
+---
+name: copy_from_sp64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_from_sp64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i64 = COPY $sp64
+    ; CHECK-NEXT: RETURN [[COPY]], implicit-def $arguments
+    %0:_(i64) = COPY $sp64
+    RETURN %0(i64), implicit-def $arguments
+...
+
+---
+name: copy_to_fp32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_to_fp32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: $fp32 = COPY [[ARGUMENT_i32_]]
+    ; CHECK-NEXT: RETURN implicit-def $arguments
+    %0:i32(i32) = ARGUMENT_i32 0, implicit $arguments
+    $fp32 = COPY %0(i32)
+    RETURN implicit-def $arguments
+...
+
+---
+name: copy_to_fp64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_to_fp64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: $fp64 = COPY [[ARGUMENT_i64_]]
+    ; CHECK-NEXT: RETURN implicit-def $arguments
+    %0:i64(i64) = ARGUMENT_i64 0, implicit $arguments
+    $fp64 = COPY %0(i64)
+    RETURN implicit-def $arguments
+...
+
+---
+name: copy_from_fp32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_from_fp32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i32 = COPY $fp32
+    ; CHECK-NEXT: RETURN [[COPY]], implicit-def $arguments
+    %0:_(i32) = COPY $fp32
+    RETURN %0(i32), implicit-def $arguments
+...
+
+---
+name: copy_from_fp64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_from_fp64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i64 = COPY $fp64
+    ; CHECK-NEXT: RETURN [[COPY]], implicit-def $arguments
+    %0:_(i64) = COPY $fp64
+    RETURN %0(i64), implicit-def $arguments
+...
+
+---
+name: copy_i32_to_f32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_i32_to_f32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[F32_REINTERPRET_I32_:%[0-9]+]]:f32 = F32_REINTERPRET_I32 [[ARGUMENT_i32_]]
+    ; CHECK-NEXT: RETURN [[F32_REINTERPRET_I32_]], implicit-def $arguments
+    %0:i32 = ARGUMENT_i32 0, implicit $arguments
+    %1:f32 = COPY %0
+    RETURN %1, implicit-def $arguments
+...
+
+---
+name: copy_f32_to_i32
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_f32_to_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_f32_:%[0-9]+]]:f32 = ARGUMENT_f32 0, implicit $arguments
+    ; CHECK-NEXT: [[I32_REINTERPRET_F32_:%[0-9]+]]:i32 = I32_REINTERPRET_F32 [[ARGUMENT_f32_]]
+    ; CHECK-NEXT: RETURN [[I32_REINTERPRET_F32_]], implicit-def $arguments
+    %0:f32 = ARGUMENT_f32 0, implicit $arguments
+    %1:i32 = COPY %0
+    RETURN %1, implicit-def $arguments
+...
+
+---
+name: copy_i64_to_f64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_i64_to_f64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[F64_REINTERPRET_I64_:%[0-9]+]]:f64 = F64_REINTERPRET_I64 [[ARGUMENT_i64_]]
+    ; CHECK-NEXT: RETURN [[F64_REINTERPRET_I64_]], implicit-def $arguments
+    %0:i64 = ARGUMENT_i64 0, implicit $arguments
+    %1:f64 = COPY %0
+    RETURN %1, implicit-def $arguments
+...
+
+---
+name: copy_f64_to_f64
+legalized: true
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: copy_f64_to_f64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_f64_:%[0-9]+]]:f64 = ARGUMENT_f64 0, implicit $arguments
+    ; CHECK-NEXT: [[I64_REINTERPRET_F64_:%[0-9]+]]:i64 = I64_REINTERPRET_F64 [[ARGUMENT_f64_]]
+    ; CHECK-NEXT: RETURN [[I64_REINTERPRET_F64_]], implicit-def $arguments
+    %0:f64 = ARGUMENT_f64 0, implicit $arguments
+    %1:i64 = COPY %0
+    RETURN %1, implicit-def $arguments
+...


        


More information about the llvm-commits mailing list