using System; using System.Collections.Generic; using Translator.Core.Analysis.Ssa; using Translator.Core.Analysis.Representation; using Translator.Core.Analysis; using Translator.Core.CodeGen; using Translator.Core.Ir; using Translator.Core.Representation; using Xunit; namespace Translator.Tests; public class ResidentAbiCodeGenTests { [Fact] public void StateFreeLeafVariantHasOnlyLiveNativeInputsAndNoCpuContext() { var function = new IrFunction("state_free_store", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrStore(new IrAddress("r3", 4), IrValue.Register("r4"), 4), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( 0x80063FF0, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_store", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary { ["r3"] = ValueRepresentation.UInt32, ["r4"] = ValueRepresentation.UInt32 }), emitStateFreeLeafVariant: true, stateFreeCallSymbols: new Dictionary { [0x80063FF0] = "state_free_store_native" }, stateFreeEntryVariants: new[] { new GuestStateFreeCallVariant( 0x80063FF0, "state_free_store_native_v0", GuestAbiContractAnalyzer.Analyze(function)) }); var variantStart = code.IndexOf( "state_free_store_native(uint32_t native_r3, uint32_t native_r4)", StringComparison.Ordinal); Assert.True(variantStart >= 0, code); var markerEnd = code.IndexOf('\n', code.IndexOf("RECOMP_STATE_FREE_ABI", variantStart, StringComparison.Ordinal)); var variant = code[variantStart..markerEnd]; Assert.Contains("uint32_t native_r3, uint32_t native_r4", variant, StringComparison.Ordinal); Assert.DoesNotContain("CpuContext", variant, StringComparison.Ordinal); Assert.DoesNotContain("ctx->", variant, StringComparison.Ordinal); Assert.DoesNotContain("native_r5", variant, StringComparison.Ordinal); Assert.Contains("RECOMP_STATE_FREE_ABI", variant, StringComparison.Ordinal); Assert.Contains( "extern \"C\" MKW_PPC_ALWAYS_INLINE_BODY void state_free_store_native_v0(", code, StringComparison.Ordinal); var publicStart = code.IndexOf( "extern \"C\" void state_free_store(CpuContext* MKW_RESTRICT ctx)", StringComparison.Ordinal); var publicEnd = code.IndexOf( "extern \"C\" MKW_PPC_ALWAYS_INLINE_BODY", publicStart, StringComparison.Ordinal); var publicBody = code[publicStart..publicEnd]; Assert.Contains("r3", publicBody, StringComparison.Ordinal); Assert.DoesNotContain("cached_r3", publicBody, StringComparison.Ordinal); Assert.Contains("cached_r3", variant, StringComparison.Ordinal); } [Fact] public void StateFreeLeafElidesDeadLrAssignmentWithNoncanonicalSource() { const uint address = 0x80063FE0u; var function = new IrFunction("state_free_dead_lr", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrAssign("lr", IrValue.Register("r0")), new IrReturn(null) }) }); var planned = GuestAbiContractAnalyzer.Analyze(function) with { MayWriteLr = false }; var code = new CxxLinearCodeGenerator().Emit( address, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_dead_lr", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary { ["r0"] = ValueRepresentation.UInt32, ["lr"] = ValueRepresentation.UInt32 }), emitStateFreeLeafVariant: true, stateFreeAbiContracts: new Dictionary { [address] = planned }, stateFreeCallSymbols: new Dictionary { [address] = "state_free_dead_lr_native" }); var start = code.IndexOf("state_free_dead_lr_native(", StringComparison.Ordinal); var marker = code.IndexOf("RECOMP_STATE_FREE_ABI", start, StringComparison.Ordinal); var variant = code[start..marker]; Assert.DoesNotContain("ctx->lr", variant, StringComparison.Ordinal); Assert.DoesNotContain("CpuContext", variant, StringComparison.Ordinal); } [Fact] public void DirectStateFreeCallUsesLiveArgumentsAndRetainsNormalAbiFallback() { const uint calleeAddress = 0x80063FF0u; var function = new IrFunction("state_free_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var contract = new GuestAbiContract( GprReadBeforeWriteMask: (1u << 3) | (1u << 4), GprPossibleWriteMask: 0, GprReturnMask: 0, FprReadBeforeWriteMask: 0, FprPossibleWriteMask: 0, FprReturnMask: 0, CrReadBeforeWriteMask: 0, CrPossibleWriteMask: 0, ReadsXerBeforeWrite: false, MayWriteXer: false, ReadsCtrBeforeWrite: false, MayWriteCtr: false, ReadsLrBeforeWrite: false, MayWriteLr: false, BoundaryFlags: GuestCallBoundaryFlags.None, DirectCallTargets: Array.Empty()); var code = new CxxLinearCodeGenerator().Emit( 0x80063FE0, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), // The caller itself is emitted as a state-free interface so its own // architectural GPRs live in native locals; that is what makes the // direct state-free call site pass cached values instead of context. emitStateFreeLeafVariant: true, stateFreeAbiContracts: new Dictionary { [calleeAddress] = contract, [0x80063FE0u] = contract }, stateFreeCallSymbols: new Dictionary { [calleeAddress] = "state_free_store_native", [0x80063FE0u] = "state_free_caller_native" }); Assert.Contains($"IsBaseTranslatedCpuTargetActive<0x{calleeAddress:X8}u>()", code, StringComparison.Ordinal); Assert.Contains($"InvokeDirectCpu<0x{calleeAddress:X8}u>(ctx);", code, StringComparison.Ordinal); // Inside the caller's own state-free body the call site must consume the // native register locals; the retained public CpuContext entry keeps the // ordinary ctx-based fallback and is deliberately excluded here. var variantStart = code.IndexOf("void state_free_caller_native(", StringComparison.Ordinal); Assert.True(variantStart >= 0, "Missing state-free variant for the caller."); var variant = code[variantStart..code.IndexOf("RECOMP_STATE_FREE_ABI", variantStart, StringComparison.Ordinal)]; Assert.Contains("state_free_store_native(cached_r3, cached_r4);", variant, StringComparison.Ordinal); Assert.DoesNotContain("state_free_store_native(ctx", variant, StringComparison.Ordinal); } [Fact] public void OversizedStateFreeRegionCannotBypassContextBoundary() { const uint calleeAddress = 0x80063FF0u; var function = new IrFunction("oversized_state_free_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var contract = new GuestAbiContract( GprReadBeforeWriteMask: 0x1Fu, GprPossibleWriteMask: 1u << 3, GprReturnMask: 1u << 3, FprReadBeforeWriteMask: 0, FprPossibleWriteMask: 0, FprReturnMask: 0, CrReadBeforeWriteMask: 0, CrPossibleWriteMask: 0, ReadsXerBeforeWrite: false, MayWriteXer: false, ReadsCtrBeforeWrite: false, MayWriteCtr: false, ReadsLrBeforeWrite: false, MayWriteLr: false, BoundaryFlags: GuestCallBoundaryFlags.None, DirectCallTargets: Array.Empty()); var code = new CxxLinearCodeGenerator().Emit( 0x80063FE0, new SsaTransformer().Convert(function), new FunctionAbiClassification("oversized_state_free_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), stateFreeAbiContracts: new Dictionary { [calleeAddress] = contract }, stateFreeCallSymbols: new Dictionary { [calleeAddress] = "oversized_state_free_native" }); Assert.Contains("if (false) {", code, StringComparison.Ordinal); Assert.Contains($"InvokeDirectCpu<0x{calleeAddress:X8}u>(ctx);", code, StringComparison.Ordinal); } [Fact] public void ExactCallSiteCanSelectCompactSpecializedResultContract() { const uint calleeAddress = 0x80063FF0u; var function = new IrFunction("state_free_specialized_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrAssign("r7", IrValue.Imm(1)), new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var globalContract = new GuestAbiContract( (1u << 3) | (1u << 5), (1u << 4) | (1u << 6), 0, 0, 0, 0, 0, 0, false, false, false, false, false, false, GuestCallBoundaryFlags.None, Array.Empty()); var specializedContract = globalContract with { GprReadBeforeWriteMask = 1u << 3, GprPossibleWriteMask = 1u << 4 }; var variant = new GuestStateFreeCallVariant( calleeAddress, "state_free_store_native_v0", specializedContract); var code = new CxxLinearCodeGenerator().Emit( 0x80063FE0, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_specialized_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), stateFreeAbiContracts: new Dictionary { [calleeAddress] = globalContract }, stateFreeCallSymbols: new Dictionary { [calleeAddress] = "state_free_store_native" }, stateFreeCallSiteVariants: new Dictionary { [new GuestStateFreeCallSiteKey("entry", calleeAddress, 0)] = variant }); Assert.Contains("state_free_store_native_v0(r3)", code, StringComparison.Ordinal); Assert.Contains("state_free_store_native(r3, r5)", code, StringComparison.Ordinal); Assert.DoesNotContain("state_free_store_native_v0(r5)", code, StringComparison.Ordinal); } [Fact] public void StateFreeDirectCallPassesDefinedFallthroughLrInsteadOfContextLr() { const uint calleeAddress = 0x80063FF0u; var function = new IrFunction("state_free_lr_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrAssign("lr", IrValue.Imm(0x80064008u)), new IrCall("lr", $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var contract = new GuestAbiContract( 1u << 3, 0, 0, 0, 0, 0, 0, 0, false, false, false, false, true, false, GuestCallBoundaryFlags.None, Array.Empty()); var code = new CxxLinearCodeGenerator().Emit( 0x80064000u, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_lr_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), stateFreeAbiContracts: new Dictionary { [calleeAddress] = contract }, stateFreeCallSymbols: new Dictionary { [calleeAddress] = "state_free_lr_native" }); Assert.Contains("state_free_lr_native(r3, 0x80064008u)", code, StringComparison.Ordinal); Assert.DoesNotContain("state_free_lr_native(r3, ctx->lr)", code, StringComparison.Ordinal); } [Fact] public void StateFreeLeafReturnsExplicitGprAndFprStateWithoutCpuContext() { var function = new IrFunction("state_free_results", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrAssign("r3", IrValue.Register("r4")), new IrAssign("f1", IrValue.Register("f2")), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( 0x80063FD0, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_results", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary { ["r3"] = ValueRepresentation.UInt32, ["r4"] = ValueRepresentation.UInt32, ["f1"] = ValueRepresentation.Float64, ["f2"] = ValueRepresentation.Float64 }), emitStateFreeLeafVariant: true, stateFreeCallSymbols: new Dictionary { [0x80063FD0] = "state_free_results_native" }); var variantStart = code.IndexOf("MkwStateFreeResult2 state_free_results_native(", StringComparison.Ordinal); var markerEnd = code.IndexOf('\n', code.IndexOf("RECOMP_STATE_FREE_ABI", variantStart, StringComparison.Ordinal)); var variant = code[variantStart..markerEnd]; Assert.Contains("MkwStateFreeResult2", variant, StringComparison.Ordinal); Assert.Contains("state_free_results_native(", variant, StringComparison.Ordinal); Assert.Contains("PPC_FPR native_f1", variant, StringComparison.Ordinal); Assert.Contains("PPC_FPR native_f2", variant, StringComparison.Ordinal); Assert.Contains("return { static_cast(cached_r3), cached_f1.raw };", variant, StringComparison.Ordinal); Assert.DoesNotContain("ctx->", variant, StringComparison.Ordinal); } [Fact] public void TwoValueSpecializationDoesNotReuseLargerAddressResultStruct() { const uint address = 0x80063FC0u; var function = new IrFunction("state_free_result_collision", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrAssign("r3", IrValue.Imm(1)), new IrAssign("r4", IrValue.Imm(2)), new IrAssign("r5", IrValue.Imm(3)), new IrReturn(null) }) }); var publicContract = GuestAbiContractAnalyzer.Analyze(function); var compactContract = publicContract with { GprPossibleWriteMask = (1u << 3) | (1u << 4) }; var code = new CxxLinearCodeGenerator().Emit( address, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_result_collision", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), emitStateFreeLeafVariant: true, stateFreeAbiContracts: new Dictionary { [address] = publicContract }, stateFreeCallSymbols: new Dictionary { [address] = "state_free_result_collision_native" }, stateFreeEntryVariants: new[] { new GuestStateFreeCallVariant(address, "state_free_result_collision_native_v0", compactContract) }); Assert.Contains("MkwStateFreeResult_80063FC0 state_free_result_collision_native(", code, StringComparison.Ordinal); Assert.Contains("MkwStateFreeResult2 state_free_result_collision_native_v0(", code, StringComparison.Ordinal); Assert.DoesNotContain("using MkwStateFreeResult_80063FC0", code, StringComparison.Ordinal); } [Fact] public void DirectStateFreeCallWritesOnlyExplicitResults() { const uint calleeAddress = 0x80063FD0u; var function = new IrFunction("state_free_result_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var contract = new GuestAbiContract( GprReadBeforeWriteMask: 1u << 4, GprPossibleWriteMask: 1u << 3, GprReturnMask: 1u << 3, FprReadBeforeWriteMask: 1u << 2, FprPossibleWriteMask: 1u << 1, FprReturnMask: 1u << 1, CrReadBeforeWriteMask: 0, CrPossibleWriteMask: 0, ReadsXerBeforeWrite: false, MayWriteXer: false, ReadsCtrBeforeWrite: false, MayWriteCtr: false, ReadsLrBeforeWrite: false, MayWriteLr: false, BoundaryFlags: GuestCallBoundaryFlags.None, DirectCallTargets: Array.Empty()); var code = new CxxLinearCodeGenerator().Emit( 0x80063FC0, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_result_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), stateFreeAbiContracts: new Dictionary { [calleeAddress] = contract }, stateFreeCallSymbols: new Dictionary { [calleeAddress] = "state_free_results_native" }); Assert.Matches(@"const auto state_free_result_[0-9A-F_]+ = state_free_results_native\(", code); Assert.Matches(@"r3 = static_cast\(state_free_result_[0-9A-F_]+\[0\]\);", code); Assert.Matches(@"f1\.raw = static_cast\(state_free_result_[0-9A-F_]+\[1\]\);", code); Assert.DoesNotContain("r4 = state_free_result", code, StringComparison.Ordinal); Assert.DoesNotContain("f2 = state_free_result", code, StringComparison.Ordinal); } [Fact] public void DirectStateFreeCallExtractsRawBitsFromStructFprResults() { const uint calleeAddress = 0x80063FBCu; var function = new IrFunction("state_free_struct_result_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var contract = new GuestAbiContract( 0, 0, 0, 0, (1u << 0) | (1u << 2) | (1u << 3) | (1u << 4), 0, 0, 0, false, false, false, false, false, false, GuestCallBoundaryFlags.None, Array.Empty()); var code = new CxxLinearCodeGenerator().Emit( 0x80063FB8, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_struct_result_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), stateFreeAbiContracts: new Dictionary { [calleeAddress] = contract }, stateFreeCallSymbols: new Dictionary { [calleeAddress] = "state_free_struct_native" }); Assert.Matches(@"f0\.raw = static_cast\(state_free_result_[0-9A-F_]+\.f0\.raw\);", code); Assert.Matches(@"f4\.raw = static_cast\(state_free_result_[0-9A-F_]+\.f4\.raw\);", code); } [Fact] public void NonLeafStateFreeVariantCallsNativeCalleeWithoutContextFallback() { const uint callerAddress = 0x80063FB0u; const uint calleeAddress = 0x80063FA0u; var callee = new GuestAbiContract( 1u << 3, 1u << 4, 1u << 4, 0, 0, 0, 0, 0, false, false, false, false, false, false, GuestCallBoundaryFlags.None, Array.Empty()); var caller = new GuestAbiContract( 1u << 3, (1u << 4) | (1u << 5), 0, 0, 0, 0, 0, 0, false, false, false, false, false, false, GuestCallBoundaryFlags.None, new[] { calleeAddress }); var function = new IrFunction("state_free_nonleaf", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall("lr", $"0x{calleeAddress:X8}", Array.Empty()), new IrAssign("r5", IrValue.Register("r4")), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( callerAddress, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_nonleaf", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), emitStateFreeLeafVariant: true, guestAbiContracts: new Dictionary { [calleeAddress] = callee }, stateFreeAbiContracts: new Dictionary { [callerAddress] = caller with { GprPossibleWriteMask = 1u << 5 }, [calleeAddress] = callee }, stateFreeCallSymbols: new Dictionary { [callerAddress] = "state_free_nonleaf_native", [calleeAddress] = "state_free_callee_native" }); var start = code.IndexOf("state_free_nonleaf_native(", StringComparison.Ordinal); var marker = code.IndexOf("RECOMP_STATE_FREE_ABI", start, StringComparison.Ordinal); var variant = code[start..marker]; Assert.Contains("state_free_callee_native(", variant, StringComparison.Ordinal); Assert.Contains("return static_cast(cached_r5);", variant, StringComparison.Ordinal); Assert.DoesNotContain("CpuContext", variant, StringComparison.Ordinal); Assert.DoesNotContain("ctx->", variant, StringComparison.Ordinal); Assert.DoesNotContain("InvokeDirectCpu", variant, StringComparison.Ordinal); Assert.DoesNotContain("KnownTranslatedCpuCall", variant, StringComparison.Ordinal); } [Fact] public void StateFreeTailCallerForwardsIncomingLrWithoutContextAccess() { const uint callerAddress = 0x80063FB0u; const uint calleeAddress = 0x80063FA0u; var callee = new GuestAbiContract( 1u << 3, 1u << 3, 1u << 3, 0, 0, 0, 0, 0, false, false, false, false, true, false, GuestCallBoundaryFlags.None, Array.Empty()); var caller = new GuestAbiContract( 1u << 3, 1u << 3, 1u << 3, 0, 0, 0, 0, 0, false, false, false, false, false, false, GuestCallBoundaryFlags.None, new[] { calleeAddress }); var function = new IrFunction("state_free_tail_caller", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, $"0x{calleeAddress:X8}", Array.Empty()), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( callerAddress, new SsaTransformer().Convert(function), new FunctionAbiClassification("state_free_tail_caller", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), emitStateFreeLeafVariant: true, guestAbiContracts: new Dictionary { [calleeAddress] = callee }, stateFreeAbiContracts: new Dictionary { [callerAddress] = caller, [calleeAddress] = callee }, stateFreeCallSymbols: new Dictionary { [callerAddress] = "state_free_tail_caller_native", [calleeAddress] = "state_free_tail_callee_native" }); var start = code.IndexOf("state_free_tail_caller_native(", StringComparison.Ordinal); var marker = code.IndexOf("RECOMP_STATE_FREE_ABI", start, StringComparison.Ordinal); var variant = code[start..marker]; Assert.Contains("uint32_t native_lr", variant, StringComparison.Ordinal); Assert.Contains("state_free_tail_callee_native(cached_r3, native_lr)", variant, StringComparison.Ordinal); Assert.DoesNotContain("ctx->", variant, StringComparison.Ordinal); } [Fact] public void PublicLeafDoesNotElideNonvolatileGprStackRoundTrip() { var function = new IrFunction("public_leaf_gpr_save", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrStore(new IrAddress("r1", 12), IrValue.Register("r31"), 4), new IrAssign("r31", IrValue.Imm(7)), new IrLoad("r31", new IrAddress("r1", 12), 4), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( 0x80064008, new SsaTransformer().Convert(function), new FunctionAbiClassification("public_leaf_gpr_save", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary()), enableLeafAbiSpillElision: true); Assert.Contains("FlatWriteRam32", code, StringComparison.Ordinal); Assert.Contains("FlatRead32", code, StringComparison.Ordinal); Assert.Contains("r31 = 7", code, StringComparison.Ordinal); Assert.Contains("r31 = MemoryInline::FlatRead32", code, StringComparison.Ordinal); } [Fact] public void GprOnlyLeafCacheDoesNotImplicitlyElideGuestAbiSpills() { var function = new IrFunction("cached_leaf_gpr_save", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrStore(new IrAddress("r1", 12), IrValue.Register("r31"), 4), new IrAssign("r31", IrValue.Imm(7)), new IrLoad("r31", new IrAddress("r1", 12), 4), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( 0x80064010, new SsaTransformer().Convert(function), new FunctionAbiClassification("cached_leaf_gpr_save", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary())); Assert.Contains("FlatWriteRam32", code, StringComparison.Ordinal); Assert.Contains("FlatRead32", code, StringComparison.Ordinal); } [Fact] public void StackFastPathFollowsFramePointerCopiedIntoArchitecturalGpr() { var function = new IrFunction("derived_stack_gpr", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrBinary("r1", IrValue.Register("r1"), IrValue.Imm(64), "sub"), new IrBinary("r11", IrValue.Register("r1"), IrValue.Imm(64), "add"), new IrStore(new IrAddress("r11", -28), IrValue.Register("r25"), 4), new IrLoad("r25", new IrAddress("r11", -28), 4), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( 0x80064014, new SsaTransformer().Convert(function), new FunctionAbiClassification("derived_stack_gpr", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary())); // A stack slot store skips the MMIO policy check (FlatWriteRam) instead of the general guarded // write (FlatWrite); reads have no such distinction, both spell as FlatRead. Assert.Contains("MemoryInline::FlatWriteRam32", code, StringComparison.Ordinal); Assert.Contains("MemoryInline::FlatRead32", code, StringComparison.Ordinal); Assert.DoesNotContain("MemoryInline::FlatWrite32", code, StringComparison.Ordinal); } [Fact] public void StackFastPathFollowsMultipleSsaDerivedAddressRegisters() { var function = new IrFunction("transitive_stack_gpr", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrAssign("r11", IrValue.Register("r1")), new IrBinary("r12", IrValue.Register("r11"), IrValue.Imm(32), "add"), new IrStore(new IrAddress("r12", -8), IrValue.Register("r31"), 4), new IrLoad("r31", new IrAddress("r12", -8), 4), new IrReturn(null) }) }); var code = new CxxLinearCodeGenerator().Emit( 0x80064018, new SsaTransformer().Convert(function), new FunctionAbiClassification("transitive_stack_gpr", ValueRepresentation.Void), new RepresentationEnvironment(new Dictionary())); // See StackFastPathFollowsFramePointerCopiedIntoArchitecturalGpr: only // the store side still distinguishes the stack fast path in the emitted // helper name. Assert.Contains("MemoryInline::FlatWriteRam32", code, StringComparison.Ordinal); Assert.Contains("MemoryInline::FlatRead32", code, StringComparison.Ordinal); Assert.DoesNotContain("MemoryInline::FlatWrite32", code, StringComparison.Ordinal); } [Fact] public void GuestAbiContractModelsInlineGprThunkRegisterEffects() { var function = new IrFunction("thunk_contract", "entry", new[] { new IrBasicBlock("entry", new IrInstruction[] { new IrCall(string.Empty, "0x80021580", Array.Empty()), // save r19-r31 new IrCall(string.Empty, "0x800215CC", Array.Empty()), // rest r19-r31 new IrReturn(null) }) }); var contract = GuestAbiContractAnalyzer.Analyze(function); const uint expected = 0xFFF80000u; Assert.Equal(expected, contract.GprReadBeforeWriteMask & expected); Assert.Equal(expected, contract.GprPossibleWriteMask & expected); Assert.Empty(contract.DirectCallTargets); } }