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. ///