Skip to content

Commit

Permalink
Fixed IfConversion. (#1081)
Browse files Browse the repository at this point in the history
* Extended PhiBindings analysis to check for individual branch targets.
* Fixed IfConversion transformation phasing possibilities of endless loop branches.
* Fixed PTXCodeGenerator to emit individual target bindings.
* Added new test case for improved IfConversion transformation.
  • Loading branch information
m4rs-mt authored Sep 7, 2023
1 parent bfa942d commit d1dee9f
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 79 deletions.
57 changes: 48 additions & 9 deletions Src/ILGPU.Tests/BasicJumps.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------------------------
// ILGPU
// Copyright (c) 2021 ILGPU Project
// Copyright (c) 2021-2023 ILGPU Project
// www.ilgpu.net
//
// File: BasicJumps.cs
Expand All @@ -16,6 +16,7 @@
using Xunit.Abstractions;

#pragma warning disable CS0162
#pragma warning disable CS0164

namespace ILGPU.Tests
{
Expand Down Expand Up @@ -164,25 +165,63 @@ internal static void BasicNestedLoopJumpKernel(
}

[Theory]
[InlineData(32, 0, 67)]
[InlineData(32, 2, 25)]
[InlineData(1024, 0, 67)]
[InlineData(1024, 2, 25)]
[InlineData(0, 67)]
[InlineData(2, 25)]
[KernelMethod(nameof(BasicNestedLoopJumpKernel))]
public void BasicNestedLoopJump(int length, int c, int res)
public void BasicNestedLoopJump(int c, int res)
{
using var buffer = Accelerator.Allocate1D<int>(length);
using var source = Accelerator.Allocate1D<int>(64);
const int Length = 64;
using var buffer = Accelerator.Allocate1D<int>(Length);
using var source = Accelerator.Allocate1D<int>(Length);
var sourceData = Enumerable.Range(0, (int)source.Length).ToArray();
sourceData[57] = 23;
source.CopyFromCPU(Accelerator.DefaultStream, sourceData);

Execute(buffer.Length, buffer.View, source.View, c);

var expected = Enumerable.Repeat(res, length).ToArray();
var expected = Enumerable.Repeat(res, Length).ToArray();
Verify(buffer.View, expected);
}

private static void BasicNestedLoopJumpKernel2(
Index1D index,
ArrayView1D<int, Stride1D.Dense> target,
ArrayView1D<int, Stride1D.Dense> source)
{
int k = 0;
entry:
for (int i = 0; i < source.Length; ++i)
{
goto exit;
}

target[index] = 42;
return;

nested:
k = 43;

exit:
if (k++ < 1)
goto entry;
target[index] = 23 + k;
}

[Fact]
[KernelMethod(nameof(BasicNestedLoopJumpKernel2))]
public void BasicNestedLoopJump2()
{
const int Length = 32;
using var buffer = Accelerator.Allocate1D<int>(Length);
using var source = Accelerator.Allocate1D<int>(Length);

Execute(buffer.Length, buffer.View, source.View);

var expected = Enumerable.Repeat(25, Length).ToArray();
Verify(buffer.View, expected);
}
}
}

#pragma warning restore CS0164
#pragma warning restore CS0162
89 changes: 88 additions & 1 deletion Src/ILGPU/Backends/PTX/PTXCodeGenerator.Terminators.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------------------------
// ILGPU
// Copyright (c) 2018-2022 ILGPU Project
// Copyright (c) 2018-2023 ILGPU Project
// www.ilgpu.net
//
// File: PTXCodeGenerator.Terminators.cs
Expand All @@ -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
{
Expand All @@ -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;
}

/// <summary>
/// Generates phi bindings for jumping to a specific target block.
/// </summary>
/// <param name="current">The current block.</param>
private void GeneratePhiBindings(BasicBlock current)
{
if (!phiBindings.TryGetBindings(current, out var bindings))
return;
BindPhis(bindings, target: null);
}

/// <summary cref="IBackendCodeGenerator.GenerateCode(UnconditionalBranch)"/>
public void GenerateCode(UnconditionalBranch branch)
{
// Bind phis
GeneratePhiBindings(branch.BasicBlock);

if (Schedule.IsImplicitSuccessor(branch.BasicBlock, branch.Target))
return;

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
115 changes: 63 additions & 52 deletions Src/ILGPU/Backends/PTX/PTXCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,11 @@ protected static string GetParameterName(Parameter parameter) =>
#region Instance

private int labelCounter;
private readonly Dictionary<BasicBlock, string> blockLookup =
new Dictionary<BasicBlock, string>();
private readonly Dictionary<(Encoding, string), string> stringConstants =
new Dictionary<(Encoding, string), string>();
private readonly Dictionary<BasicBlock, string> blockLookup = new();

private readonly Dictionary<(Encoding, string), string> stringConstants = new();
private readonly PhiBindings phiBindings;
private readonly Dictionary<Value, Register> intermediatePhiRegisters;
private readonly string labelPrefix;

/// <summary>
Expand Down Expand Up @@ -321,6 +322,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(
(_, phiValue) => Allocate(phiValue));
intermediatePhiRegisters = new Dictionary<Value, Register>(
phiBindings.MaxNumIntermediatePhis);
}

#endregion
Expand Down Expand Up @@ -485,11 +492,6 @@ protected void GenerateCodeInternal(int registerOffset)
blockLookup.Add(block, DeclareLabel());
}

// Find all phi nodes, allocate target registers and setup internal mapping
var phiBindings = Schedule.ComputePhiBindings(
(_, phiValue) => Allocate(phiValue));
var intermediatePhiRegisters = new Dictionary<Value, Register>(
phiBindings.MaxNumIntermediatePhis);
Builder.AppendLine();

// Generate code
Expand Down Expand Up @@ -524,59 +526,68 @@ 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
Builder.AppendLine("}");
Builder.Insert(registerOffset, GenerateRegisterInformation("\t"));
}

/// <summary>
/// Binds all phi values of the current block flowing through an edge to the
/// target block.
/// </summary>
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);
}
}

/// <summary>
/// Setups local or shared allocations.
/// </summary>
Expand Down
18 changes: 18 additions & 0 deletions Src/ILGPU/Backends/PhiBindings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ internal PhiBindingCollection(in BlockInfo info)

#region Methods

/// <summary>
/// Returns true if the current binding configuration needs separate bindings
/// for individual target blocks.
/// </summary>
public bool NeedSeparateBindingsFor(BasicBlock target)
{
foreach (var (phiValue, _) in this)
{
if (phiValue.BasicBlock != target &&
!phiValue.Sources.Contains(target, new BasicBlock.Comparer()))
{
return true;
}
}

return false;
}

/// <summary>
/// Returns true if the given phi is an intermediate phi value that requires
/// a temporary intermediate variable to be assigned to.
Expand Down
Loading

0 comments on commit d1dee9f

Please sign in to comment.