diff --git a/Src/ILGPU/Backends/PTX/PTXCodeGenerator.Terminators.cs b/Src/ILGPU/Backends/PTX/PTXCodeGenerator.Terminators.cs
index 090a94b57a..91a520210b 100644
--- a/Src/ILGPU/Backends/PTX/PTXCodeGenerator.Terminators.cs
+++ b/Src/ILGPU/Backends/PTX/PTXCodeGenerator.Terminators.cs
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------------------------
// ILGPU
-// Copyright (c) 2018-2022 ILGPU Project
+// Copyright (c) 2018-2023 ILGPU Project
// www.ilgpu.net
//
// File: PTXCodeGenerator.Terminators.cs
@@ -9,8 +9,10 @@
// Source License. See LICENSE.txt for details.
// ---------------------------------------------------------------------------------------
+using ILGPU.IR;
using ILGPU.IR.Values;
using ILGPU.Util;
+using System.Diagnostics.CodeAnalysis;
namespace ILGPU.Backends.PTX
{
@@ -24,15 +26,50 @@ public void GenerateCode(ReturnTerminator returnTerminator)
var resultRegister = Load(returnTerminator.ReturnValue);
EmitStoreParam(ReturnParamName, resultRegister);
}
+
Command(
Uniforms.IsUniform(returnTerminator)
? PTXInstructions.UniformReturnOperation
: PTXInstructions.ReturnOperation);
}
+ private bool NeedSeparatePhiBindings(
+ BasicBlock basicBlock,
+ BasicBlock target,
+ [NotNullWhen(true)]
+ out PhiBindings.PhiBindingCollection bindings)
+ {
+ if (!phiBindings.TryGetBindings(target, out bindings))
+ return false;
+
+ // Check whether there are bindings pointing to different blocks
+ foreach (var (phiValue, _) in bindings)
+ {
+ if (phiValue.BasicBlock != target)
+ return true;
+ }
+
+ // We were not able to find misleading data
+ return false;
+ }
+
+ ///
+ /// Generates phi bindings for jumping to a specific target block.
+ ///
+ /// The current block.
+ private void GeneratePhiBindings(BasicBlock current)
+ {
+ if (!phiBindings.TryGetBindings(current, out var bindings))
+ return;
+ BindPhis(bindings, target: null);
+ }
+
///
public void GenerateCode(UnconditionalBranch branch)
{
+ // Bind phis
+ GeneratePhiBindings(branch.BasicBlock);
+
if (Schedule.IsImplicitSuccessor(branch.BasicBlock, branch.Target))
return;
@@ -60,6 +97,53 @@ public void GenerateCode(IfBranch branch)
? PTXInstructions.UniformBranchOperation
: PTXInstructions.BranchOperation;
+ // Gather phi bindings and test both, true and false targets
+ if (phiBindings.TryGetBindings(branch.BasicBlock, out var bindings) &&
+ (bindings.NeedSeparateBindingsFor(trueTarget) ||
+ bindings.NeedSeparateBindingsFor(falseTarget)))
+ {
+ // We need to emit different bindings in each branch
+ if (branch.IsInverted)
+ Utilities.Swap(ref trueTarget, ref falseTarget);
+
+ // Declare a temporary jump target to skip true branches
+ var tempLabel = DeclareLabel();
+ using (var command = BeginCommand(
+ branchOperation,
+ new PredicateConfiguration(condition, isTrue: false)))
+ {
+ command.AppendLabel(tempLabel);
+ }
+
+ // Bind all true phis
+ BindPhis(bindings, trueTarget);
+
+ // Jump to true target in the current case
+ using (var command = BeginCommand(branchOperation))
+ {
+ var targetLabel = blockLookup[trueTarget];
+ command.AppendLabel(targetLabel);
+ }
+
+ // Mark the false case label and bind all values
+ MarkLabel(tempLabel);
+ BindPhis(bindings, falseTarget);
+
+ if (!Schedule.IsImplicitSuccessor(branch.BasicBlock, falseTarget))
+ {
+ // Jump to false target in the else case
+ using var command = BeginCommand(branchOperation);
+ var targetLabel = blockLookup[falseTarget];
+ command.AppendLabel(targetLabel);
+ }
+
+ // Skip further bindings an branches
+ return;
+ }
+
+ // Generate phi bindings for all blocks
+ BindPhis(bindings, target: null);
+
// The current schedule has inverted all if conditions with implicit branch
// targets to simplify the work of the PTX assembler
if (Schedule.IsImplicitSuccessor(branch.BasicBlock, trueTarget))
@@ -151,6 +235,9 @@ public void GenerateCode(SwitchBranch branch)
}
Builder.AppendLine(";");
+ // Generate all phi bindings for all cases
+ GeneratePhiBindings(branch.BasicBlock);
+
using (var command = BeginCommand(
isUniform
? PTXInstructions.UniformBranchIndexOperation
diff --git a/Src/ILGPU/Backends/PTX/PTXCodeGenerator.cs b/Src/ILGPU/Backends/PTX/PTXCodeGenerator.cs
index bd9436f9eb..bda10d8ed7 100644
--- a/Src/ILGPU/Backends/PTX/PTXCodeGenerator.cs
+++ b/Src/ILGPU/Backends/PTX/PTXCodeGenerator.cs
@@ -317,10 +317,11 @@ protected static string GetParameterName(Parameter parameter) =>
#region Instance
private int labelCounter;
- private readonly Dictionary blockLookup =
- new Dictionary();
- private readonly Dictionary<(Encoding, string), string> stringConstants =
- new Dictionary<(Encoding, string), string>();
+ private readonly Dictionary blockLookup = new();
+
+ private readonly Dictionary<(Encoding, string), string> stringConstants = new();
+ private readonly PhiBindings phiBindings;
+ private readonly Dictionary intermediatePhiRegisters;
private readonly string labelPrefix;
///
@@ -352,6 +353,12 @@ internal PTXCodeGenerator(in GeneratorArgs args, Method method, Allocas allocas)
args.Properties.GetPTXBackendMode() == PTXBackendMode.Enhanced
? Method.Blocks.CreateOptimizedPTXSchedule()
: Method.Blocks.CreateDefaultPTXSchedule();
+
+ // Create phi bindings and initialize temporary phi registers
+ phiBindings = Schedule.ComputePhiBindings(
+ new PhiBindingAllocator(this));
+ intermediatePhiRegisters = new Dictionary(
+ phiBindings.MaxNumIntermediatePhis);
}
#endregion
@@ -517,10 +524,6 @@ protected void GenerateCodeInternal(int registerOffset)
}
// Find all phi nodes, allocate target registers and setup internal mapping
- var phiBindings = Schedule.ComputePhiBindings(
- new PhiBindingAllocator(this));
- var intermediatePhiRegisters = new Dictionary(
- phiBindings.MaxNumIntermediatePhis);
Builder.AppendLine();
// Generate code
@@ -555,52 +558,14 @@ protected void GenerateCodeInternal(int registerOffset)
DebugInfoGenerator.ResetLocation();
- // Wire phi nodes
- if (phiBindings.TryGetBindings(block, out var bindings))
- {
- // Assign all phi values
- foreach (var (phiValue, value) in bindings)
- {
- // Load the current phi target register
- var phiTargetRegister = Load(phiValue);
-
- // Check for an intermediate phi value
- if (bindings.IsIntermediate(phiValue))
- {
- var intermediateRegister = AllocateType(phiValue.Type);
- intermediatePhiRegisters.Add(phiValue, intermediateRegister);
-
- // Move this phi value into a temporary register for reuse
- EmitComplexCommand(
- PTXInstructions.MoveOperation,
- new PhiMoveEmitter(),
- intermediateRegister,
- phiTargetRegister);
- }
-
- // Determine the source value from which we need to copy from
- var sourceRegister = intermediatePhiRegisters
- .TryGetValue(value, out var tempRegister)
- ? tempRegister
- : Load(value);
-
- // Move contents
- EmitComplexCommand(
- PTXInstructions.MoveOperation,
- new PhiMoveEmitter(),
- phiTargetRegister,
- sourceRegister);
- }
-
- // Free temporary registers
- foreach (var register in intermediatePhiRegisters.Values)
- Free(register);
- intermediatePhiRegisters.Clear();
- }
-
// Build terminator
this.GenerateCodeFor(block.Terminator.AsNotNull());
Builder.AppendLine();
+
+ // Free temporary registers
+ foreach (var register in intermediatePhiRegisters.Values)
+ Free(register);
+ intermediatePhiRegisters.Clear();
}
// Finish function and append register information
@@ -608,6 +573,53 @@ protected void GenerateCodeInternal(int registerOffset)
Builder.Insert(registerOffset, GenerateRegisterInformation("\t"));
}
+ ///
+ /// Binds all phi values of the current block flowing through an edge to the
+ /// target block.
+ ///
+ private void BindPhis(
+ PhiBindings.PhiBindingCollection bindings,
+ BasicBlock? target)
+ {
+ // Assign all phi values
+ foreach (var (phiValue, value) in bindings)
+ {
+ // Reject phis not flowing to the target edge
+ if (target is not null && phiValue.BasicBlock != target)
+ continue;
+
+ // Load the current phi target register
+ var phiTargetRegister = Load(phiValue);
+
+ // Check for an intermediate phi value
+ if (bindings.IsIntermediate(phiValue))
+ {
+ var intermediateRegister = AllocateType(phiValue.Type);
+ intermediatePhiRegisters.Add(phiValue, intermediateRegister);
+
+ // Move this phi value into a temporary register for reuse
+ EmitComplexCommand(
+ PTXInstructions.MoveOperation,
+ new PhiMoveEmitter(),
+ intermediateRegister,
+ phiTargetRegister);
+ }
+
+ // Determine the source value from which we need to copy from
+ var sourceRegister = intermediatePhiRegisters
+ .TryGetValue(value, out var tempRegister)
+ ? tempRegister
+ : Load(value);
+
+ // Move contents
+ EmitComplexCommand(
+ PTXInstructions.MoveOperation,
+ new PhiMoveEmitter(),
+ phiTargetRegister,
+ sourceRegister);
+ }
+ }
+
///
/// Setups local or shared allocations.
///