mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-09 18:26:09 -04:00
279ce8328f
* macos: add x86_64 Intel support * (feat) Apple Silicon macOS CI building and Setup.pkg documentation (#118) * docs(macos): document Setup.pkg installation Add Apple Silicon macOS CI coverage for runtime configuration, substrate tests, and Setup.pkg packaging alongside the macOS installation instructions. * macos: pin Apple Silicon deployment target * fix(macos): restore Retro Rewind local builds * macos: package universal setup tools * ci: build macOS input expression tests * runtime: Do not force 14.0 minimum anymore new minimum is 12.0 * macos: support older libc++ algorithms * macos: allow undefined MTLLogStateDescriptor for older SDKs * tests: deflake input expression timing window test * build: Fix mac tests * translator: emit null statement after continuation labels for C++17 compatibility * feat(macos): add MetalFX spatial upscaling * Fix automatic music muting on macOS Co-Authored-By: Michael G <10155689+DarthMDev@users.noreply.github.com> Co-Authored-By: Daan Vervacke <23398694+DaanVervacke@users.noreply.github.com> * Address CodeRabbit review comments and integrate upstream TLS cmake * Remove version requirement for running package CI Co-Authored-By: Michael G <10155689+DarthMDev@users.noreply.github.com> * publish-app: fix dependency discovery from build dir, handle spaces in paths, and fail on missing deps * publish-app: preserve subdirectory suffix during @rpath dependency lookup * cmake: synchronize CMAKE_SYSTEM_PROCESSOR with CMAKE_OSX_ARCHITECTURES on macOS * fix rpaths --------- Co-authored-by: DarthM <mgracer48@yahoo.com> Co-authored-by: Michael G <10155689+DarthMDev@users.noreply.github.com> Co-authored-by: Daan Vervacke <23398694+DaanVervacke@users.noreply.github.com> Co-authored-by: patchzyy <64382339+patchzyy@users.noreply.github.com>
188 lines
7.8 KiB
C#
188 lines
7.8 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Shape of the emitted text. These assertions are about readability of the
|
|
/// generated C++; each one is semantics-preserving by construction.
|
|
/// </summary>
|
|
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<string, ValueRepresentation>();
|
|
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<uint8_t>(r3));", code, StringComparison.Ordinal);
|
|
Assert.Contains("MemoryInline::FlatWrite16((r4 + 4), static_cast<uint16_t>(r3));", code, StringComparison.Ordinal);
|
|
Assert.Contains("MemoryInline::FlatWrite32((r4 + 8), r3);", code, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("static_cast<uint32_t>(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<string, ValueRepresentation>
|
|
{
|
|
["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);
|
|
}
|
|
|
|
[Fact]
|
|
public void ContinuationLabelAtBlockEndEmitsValidCxx17Statement()
|
|
{
|
|
var function = new IrFunction("continuation_at_block_end", "0x800E7798", new[]
|
|
{
|
|
new IrBasicBlock("0x800E7798", new IrInstruction[]
|
|
{
|
|
new IrCall(string.Empty, "0x8179B000", System.Array.Empty<IrValue>()),
|
|
new IrTracePpc(0x800E77A0u, "nop", "0x60000000"),
|
|
new IrJump("0x800E77A4")
|
|
}),
|
|
new IrBasicBlock("0x800E77A4", new IrInstruction[]
|
|
{
|
|
new IrReturn(null)
|
|
})
|
|
});
|
|
|
|
var types = new RepresentationEnvironment(new Dictionary<string, ValueRepresentation>());
|
|
var code = new CxxLinearCodeGenerator().Emit(
|
|
0x800E7798,
|
|
new SsaTransformer().Convert(function),
|
|
new FunctionAbiClassification(function.Name, ValueRepresentation.Void),
|
|
types,
|
|
lrContinuationCallTargets: new HashSet<uint> { 0x8179B000u });
|
|
|
|
// In C++17, a label before a closing brace is invalid without an intervening statement.
|
|
Assert.Contains("loc_800E77A0: ;", code, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("loc_800E77A0:\n}", code.Replace("\r\n", "\n"), StringComparison.Ordinal);
|
|
}
|
|
}
|
|
|