using System.Collections.Generic;
using Translator.Core.Analysis.Representation;
using Translator.Core.Analysis.Ssa;
using Translator.Core.CodeGen;
using Translator.Core.Ir;
using Translator.Core.Representation;
using Xunit;
namespace Translator.Tests;
///
/// Shape of the emitted text. These assertions are about readability of the
/// generated C++; each one is semantics-preserving by construction.
///
public class EmittedOutputShapeTests
{
private static string Emit(IrFunction function, RepresentationEnvironment types, uint entryPoint = 0x80001000u) =>
new CxxLinearCodeGenerator().Emit(
entryPoint,
new SsaTransformer().Convert(function),
new FunctionAbiClassification(function.Name, ValueRepresentation.Void),
types);
private static RepresentationEnvironment UInt32Registers(params string[] names)
{
var map = new Dictionary();
foreach (var name in names) map[name] = ValueRepresentation.UInt32;
return new RepresentationEnvironment(map);
}
[Fact]
public void BranchWithAPureFallthroughFalseEdgeOmitsTheElseBlock()
{
var function = new IrFunction("branch_fallthrough", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
new IrSetCrField(0, IrValue.Register("r3"), IrValue.Imm(0), false),
new IrBranch("beq", "taken", "next", "cr0")
}),
new IrBasicBlock("next", new IrInstruction[]
{
new IrBinary("r3", IrValue.Register("r3"), IrValue.Imm(1), "add"),
new IrReturn(null)
}),
new IrBasicBlock("taken", new IrInstruction[] { new IrReturn(null) })
});
var code = Emit(function, UInt32Registers("r3"));
Assert.Contains("goto loc_taken;", code, StringComparison.Ordinal);
// The false edge falls through to the next emitted block and needs no
// representation normalization, so there is nothing to put in an else.
Assert.DoesNotContain("} else {", code, StringComparison.Ordinal);
}
[Fact]
public void BranchWithARealFalseEdgeKeepsTheElseBlock()
{
var function = new IrFunction("branch_two_gotos", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
new IrSetCrField(0, IrValue.Register("r3"), IrValue.Imm(0), false),
new IrBranch("beq", "taken", "other", "cr0")
}),
new IrBasicBlock("filler", new IrInstruction[] { new IrReturn(null) }),
new IrBasicBlock("taken", new IrInstruction[] { new IrReturn(null) }),
new IrBasicBlock("other", new IrInstruction[] { new IrReturn(null) })
});
var code = Emit(function, UInt32Registers("r3"));
Assert.Contains("} else {", code, StringComparison.Ordinal);
Assert.Contains("goto loc_other;", code, StringComparison.Ordinal);
}
[Fact]
public void AddressConstantsAssignedToUnsignedRegistersPrintAsHex()
{
var function = new IrFunction("address_constants", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
// 0x801B4ABC as a signed int immediate.
new IrAssign("lr", IrValue.Imm(unchecked((int)0x801B4ABCu))),
new IrAssign("r4", IrValue.Imm(unchecked((int)0x80000000u))),
// Not an address: a genuinely signed small immediate, and a
// negative value outside the guest address window.
new IrBinary("r3", IrValue.Register("r3"), IrValue.Imm(-16), "add"),
new IrAssign("r5", IrValue.Imm(-1)),
new IrReturn(null)
})
});
var code = Emit(function, UInt32Registers("r3", "r4", "r5"));
Assert.Contains("ctx->lr = 0x801B4ABCu;", code, StringComparison.Ordinal);
Assert.Contains("r4 = 0x80000000u;", code, StringComparison.Ordinal);
Assert.Contains("r3 = (r3 + -16);", code, StringComparison.Ordinal);
Assert.Contains("r5 = -1;", code, StringComparison.Ordinal);
}
[Fact]
public void SubWordStoresCastToTheExactMemoryParameterType()
{
var function = new IrFunction("sub_word_stores", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
new IrStore(new IrAddress("r4", 0), IrValue.Register("r3"), 1),
new IrStore(new IrAddress("r4", 4), IrValue.Register("r3"), 2),
new IrStore(new IrAddress("r4", 8), IrValue.Register("r3"), 4),
new IrReturn(null)
})
});
var code = Emit(function, UInt32Registers("r3", "r4"));
Assert.Contains("MemoryInline::FlatWrite8(r4, static_cast(r3));", code, StringComparison.Ordinal);
Assert.Contains("MemoryInline::FlatWrite16((r4 + 4), static_cast(r3));", code, StringComparison.Ordinal);
Assert.Contains("MemoryInline::FlatWrite32((r4 + 8), r3);", code, StringComparison.Ordinal);
Assert.DoesNotContain("static_cast(r3)", code, StringComparison.Ordinal);
}
[Fact]
public void FloatFlatHelpersPreserveTheWidthMapping()
{
var function = new IrFunction("float_width_helpers", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
new IrStore(new IrAddress("r4", 0), IrValue.Register("f1"), 4),
new IrStore(new IrAddress("r4", 8), IrValue.Register("f2"), 8),
new IrLoad("f3", new IrAddress("r4", 16), 4),
new IrLoad("f4", new IrAddress("r4", 24), 8),
new IrReturn(null)
})
});
var types = new RepresentationEnvironment(new Dictionary
{
["r4"] = ValueRepresentation.UInt32,
["f1"] = ValueRepresentation.Float64,
["f2"] = ValueRepresentation.Float64,
["f3"] = ValueRepresentation.Float64,
["f4"] = ValueRepresentation.Float64
});
var code = Emit(function, types);
Assert.Contains("MemoryInline::FlatWriteFloat32(r4, f1.d);", code, StringComparison.Ordinal);
Assert.Contains("MemoryInline::FlatWriteFloat64((r4 + 8), f2.d);", code, StringComparison.Ordinal);
Assert.Contains("f3.d = MemoryInline::FlatReadFloat32((r4 + 16));", code, StringComparison.Ordinal);
Assert.Contains("f4.d = MemoryInline::FlatReadFloat64((r4 + 24));", code, StringComparison.Ordinal);
}
}