Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISCV][GISel] Select G_FENCE. #73184

Merged
merged 2 commits into from
Nov 28, 2023
Merged

[RISCV][GISel] Select G_FENCE. #73184

merged 2 commits into from
Nov 28, 2023

Conversation

topperc
Copy link
Collaborator

@topperc topperc commented Nov 22, 2023

Using IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand.

@topperc topperc changed the title [RISCV] Select G_FENCE. [RISCV][GISel] Select G_FENCE. Nov 22, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 22, 2023

@llvm/pr-subscribers-backend-risc-v

Author: Craig Topper (topperc)

Changes

Using IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand.


Full diff: https://github.com/llvm/llvm-project/pull/73184.diff

2 Files Affected:

  • (modified) llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp (+57)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll (+100)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 3c72269d1e00c2f..0ce64e1167ab0af 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -72,6 +72,8 @@ class RISCVInstructionSelector : public InstructionSelector {
                     MachineRegisterInfo &MRI) const;
   bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB,
                        MachineRegisterInfo &MRI) const;
+  bool selectFence(MachineInstr &MI, MachineIRBuilder &MIB,
+                   MachineRegisterInfo &MRI) const;
 
   ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
   ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -564,6 +566,8 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
     return selectSelect(MI, MIB, MRI);
   case TargetOpcode::G_FCMP:
     return selectFPCompare(MI, MIB, MRI);
+  case TargetOpcode::G_FENCE:
+    return selectFence(MI, MIB, MRI);
   default:
     return false;
   }
@@ -1099,6 +1103,59 @@ bool RISCVInstructionSelector::selectFPCompare(MachineInstr &MI,
   return true;
 }
 
+bool RISCVInstructionSelector::selectFence(MachineInstr &MI,
+                                           MachineIRBuilder &MIB,
+                                           MachineRegisterInfo &MRI) const {
+  AtomicOrdering FenceOrdering =
+      static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+  SyncScope::ID FenceSSID =
+      static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+
+  if (STI.hasStdExtZtso()) {
+    // The only fence that needs an instruction is a sequentially-consistent
+    // cross-thread fence.
+    if (FenceOrdering == AtomicOrdering::SequentiallyConsistent &&
+        FenceSSID == SyncScope::System) {
+      // fence rw, rw
+      MIB.buildInstr(RISCV::FENCE, {}, {})
+          .addImm(RISCVFenceField::R | RISCVFenceField::W)
+          .addImm(RISCVFenceField::R | RISCVFenceField::W);
+    } else {
+      // MEMBARRIER is a compiler barrier; it codegens to a no-op.
+      MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+    }
+  } else if (FenceSSID == SyncScope::SingleThread) {
+    // singlethread fences only synchronize with signal handlers on the same
+    // thread and thus only need to preserve instruction order, not actually
+    // enforce memory ordering.
+    MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+  } else if (FenceOrdering == AtomicOrdering::AcquireRelease) {
+    MIB.buildInstr(RISCV::FENCE_TSO, {}, {});
+  } else {
+    unsigned Pred, Succ;
+    switch (FenceOrdering) {
+    default:
+      llvm_unreachable("Unexpected ordering");
+    case AtomicOrdering::Acquire:
+      Pred = RISCVFenceField::R;
+      Succ = RISCVFenceField::R | RISCVFenceField::W;
+      break;
+    case AtomicOrdering::Release:
+      Pred = RISCVFenceField::R | RISCVFenceField::W;
+      Succ = RISCVFenceField::W;
+      break;
+    case AtomicOrdering::SequentiallyConsistent:
+      Pred = RISCVFenceField::R | RISCVFenceField::W;
+      Succ = RISCVFenceField::R | RISCVFenceField::W;
+      break;
+    }
+    MIB.buildInstr(RISCV::FENCE, {}, {}).addImm(Pred).addImm(Succ);
+  }
+
+  MI.eraseFromParent();
+  return true;
+}
+
 namespace llvm {
 InstructionSelector *
 createRISCVInstructionSelector(const RISCVTargetMachine &TM,
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll
new file mode 100644
index 000000000000000..f41a89fc4594088
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll
@@ -0,0 +1,100 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv32 -mattr=+a -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,TSO %s
+; RUN: llc -mtriple=riscv64 -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv64 -mattr=+a -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,TSO %s
+
+define void @fence_acquire() nounwind {
+; WMO-LABEL: fence_acquire:
+; WMO:       # %bb.0:
+; WMO-NEXT:    fence r, rw
+; WMO-NEXT:    ret
+;
+; TSO-LABEL: fence_acquire:
+; TSO:       # %bb.0:
+; TSO-NEXT:    #MEMBARRIER
+; TSO-NEXT:    ret
+  fence acquire
+  ret void
+}
+
+define void @fence_release() nounwind {
+; WMO-LABEL: fence_release:
+; WMO:       # %bb.0:
+; WMO-NEXT:    fence rw, w
+; WMO-NEXT:    ret
+;
+; TSO-LABEL: fence_release:
+; TSO:       # %bb.0:
+; TSO-NEXT:    #MEMBARRIER
+; TSO-NEXT:    ret
+  fence release
+  ret void
+}
+
+define void @fence_acq_rel() nounwind {
+; WMO-LABEL: fence_acq_rel:
+; WMO:       # %bb.0:
+; WMO-NEXT:    fence.tso
+; WMO-NEXT:    ret
+;
+; TSO-LABEL: fence_acq_rel:
+; TSO:       # %bb.0:
+; TSO-NEXT:    #MEMBARRIER
+; TSO-NEXT:    ret
+  fence acq_rel
+  ret void
+}
+
+define void @fence_seq_cst() nounwind {
+; CHECK-LABEL: fence_seq_cst:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    fence rw, rw
+; CHECK-NEXT:    ret
+  fence seq_cst
+  ret void
+}
+
+define void @fence_singlethread_acquire() nounwind {
+; CHECK-LABEL: fence_singlethread_acquire:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") acquire
+  ret void
+}
+
+define void @fence_singlethread_release() nounwind {
+; CHECK-LABEL: fence_singlethread_release:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") release
+  ret void
+}
+
+define void @fence_singlethread_acq_rel() nounwind {
+; CHECK-LABEL: fence_singlethread_acq_rel:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") acq_rel
+  ret void
+}
+
+define void @fence_singlethread_seq_cst() nounwind {
+; CHECK-LABEL: fence_singlethread_seq_cst:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") seq_cst
+  ret void
+}

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 22, 2023

@llvm/pr-subscribers-llvm-globalisel

Author: Craig Topper (topperc)

Changes

Using IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand.


Full diff: https://github.com/llvm/llvm-project/pull/73184.diff

2 Files Affected:

  • (modified) llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp (+57)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll (+100)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 3c72269d1e00c2f..0ce64e1167ab0af 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -72,6 +72,8 @@ class RISCVInstructionSelector : public InstructionSelector {
                     MachineRegisterInfo &MRI) const;
   bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB,
                        MachineRegisterInfo &MRI) const;
+  bool selectFence(MachineInstr &MI, MachineIRBuilder &MIB,
+                   MachineRegisterInfo &MRI) const;
 
   ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
   ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -564,6 +566,8 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
     return selectSelect(MI, MIB, MRI);
   case TargetOpcode::G_FCMP:
     return selectFPCompare(MI, MIB, MRI);
+  case TargetOpcode::G_FENCE:
+    return selectFence(MI, MIB, MRI);
   default:
     return false;
   }
@@ -1099,6 +1103,59 @@ bool RISCVInstructionSelector::selectFPCompare(MachineInstr &MI,
   return true;
 }
 
+bool RISCVInstructionSelector::selectFence(MachineInstr &MI,
+                                           MachineIRBuilder &MIB,
+                                           MachineRegisterInfo &MRI) const {
+  AtomicOrdering FenceOrdering =
+      static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+  SyncScope::ID FenceSSID =
+      static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+
+  if (STI.hasStdExtZtso()) {
+    // The only fence that needs an instruction is a sequentially-consistent
+    // cross-thread fence.
+    if (FenceOrdering == AtomicOrdering::SequentiallyConsistent &&
+        FenceSSID == SyncScope::System) {
+      // fence rw, rw
+      MIB.buildInstr(RISCV::FENCE, {}, {})
+          .addImm(RISCVFenceField::R | RISCVFenceField::W)
+          .addImm(RISCVFenceField::R | RISCVFenceField::W);
+    } else {
+      // MEMBARRIER is a compiler barrier; it codegens to a no-op.
+      MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+    }
+  } else if (FenceSSID == SyncScope::SingleThread) {
+    // singlethread fences only synchronize with signal handlers on the same
+    // thread and thus only need to preserve instruction order, not actually
+    // enforce memory ordering.
+    MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+  } else if (FenceOrdering == AtomicOrdering::AcquireRelease) {
+    MIB.buildInstr(RISCV::FENCE_TSO, {}, {});
+  } else {
+    unsigned Pred, Succ;
+    switch (FenceOrdering) {
+    default:
+      llvm_unreachable("Unexpected ordering");
+    case AtomicOrdering::Acquire:
+      Pred = RISCVFenceField::R;
+      Succ = RISCVFenceField::R | RISCVFenceField::W;
+      break;
+    case AtomicOrdering::Release:
+      Pred = RISCVFenceField::R | RISCVFenceField::W;
+      Succ = RISCVFenceField::W;
+      break;
+    case AtomicOrdering::SequentiallyConsistent:
+      Pred = RISCVFenceField::R | RISCVFenceField::W;
+      Succ = RISCVFenceField::R | RISCVFenceField::W;
+      break;
+    }
+    MIB.buildInstr(RISCV::FENCE, {}, {}).addImm(Pred).addImm(Succ);
+  }
+
+  MI.eraseFromParent();
+  return true;
+}
+
 namespace llvm {
 InstructionSelector *
 createRISCVInstructionSelector(const RISCVTargetMachine &TM,
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll
new file mode 100644
index 000000000000000..f41a89fc4594088
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll
@@ -0,0 +1,100 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv32 -mattr=+a -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,TSO %s
+; RUN: llc -mtriple=riscv64 -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv64 -mattr=+a -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,WMO %s
+; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,TSO %s
+
+define void @fence_acquire() nounwind {
+; WMO-LABEL: fence_acquire:
+; WMO:       # %bb.0:
+; WMO-NEXT:    fence r, rw
+; WMO-NEXT:    ret
+;
+; TSO-LABEL: fence_acquire:
+; TSO:       # %bb.0:
+; TSO-NEXT:    #MEMBARRIER
+; TSO-NEXT:    ret
+  fence acquire
+  ret void
+}
+
+define void @fence_release() nounwind {
+; WMO-LABEL: fence_release:
+; WMO:       # %bb.0:
+; WMO-NEXT:    fence rw, w
+; WMO-NEXT:    ret
+;
+; TSO-LABEL: fence_release:
+; TSO:       # %bb.0:
+; TSO-NEXT:    #MEMBARRIER
+; TSO-NEXT:    ret
+  fence release
+  ret void
+}
+
+define void @fence_acq_rel() nounwind {
+; WMO-LABEL: fence_acq_rel:
+; WMO:       # %bb.0:
+; WMO-NEXT:    fence.tso
+; WMO-NEXT:    ret
+;
+; TSO-LABEL: fence_acq_rel:
+; TSO:       # %bb.0:
+; TSO-NEXT:    #MEMBARRIER
+; TSO-NEXT:    ret
+  fence acq_rel
+  ret void
+}
+
+define void @fence_seq_cst() nounwind {
+; CHECK-LABEL: fence_seq_cst:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    fence rw, rw
+; CHECK-NEXT:    ret
+  fence seq_cst
+  ret void
+}
+
+define void @fence_singlethread_acquire() nounwind {
+; CHECK-LABEL: fence_singlethread_acquire:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") acquire
+  ret void
+}
+
+define void @fence_singlethread_release() nounwind {
+; CHECK-LABEL: fence_singlethread_release:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") release
+  ret void
+}
+
+define void @fence_singlethread_acq_rel() nounwind {
+; CHECK-LABEL: fence_singlethread_acq_rel:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") acq_rel
+  ret void
+}
+
+define void @fence_singlethread_seq_cst() nounwind {
+; CHECK-LABEL: fence_singlethread_seq_cst:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #MEMBARRIER
+; CHECK-NEXT:    ret
+  fence syncscope("singlethread") seq_cst
+  ret void
+}

Copy link
Contributor

@michaelmaitland michaelmaitland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind adding some documentation for the G_FENCE opcode in another patch? Looks to be missing.

return;
}

// Refer to Table A.6 in the version 2.3 draft of the RISC-V Instruction Set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is 2.3 version a thing? I can only find version 20191213 as the only versioned pdf after 2.2. My google search was riscv instruction set volume i "this is version 2.3".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not. I copied this comment from RISCVInstrInfo.td which is where I translated this from.

Using IR test to make it easier to compare with the SelectionDAG test output.
The constants operands otherwise make it harder to understand.
@topperc topperc merged commit ffcc5c7 into llvm:main Nov 28, 2023
2 of 3 checks passed
@topperc topperc deleted the pr/gisel-fence branch November 28, 2023 04:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants