Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions Copper68k/M68020Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3710,7 +3710,9 @@ private void ExecuteByteBranch(ushort opcode)

if (CheckCondition(condition))
{
State.ProgramCounter = unchecked((uint)(branchBase + displacement));
var target = unchecked((uint)(branchBase + displacement));
_instructionFrequency.RecordTakenBranch(State.LastInstructionProgramCounter, opcode, target, 2);
State.ProgramCounter = target;
CompleteTiming(M68kInstructionTimingKey.BranchByteTaken);
return;
}
Expand Down Expand Up @@ -3995,7 +3997,9 @@ private void ExecuteWordBranch(ushort opcode)

if (CheckCondition(condition))
{
State.ProgramCounter = unchecked((uint)(branchBase + displacement));
var target = unchecked((uint)(branchBase + displacement));
_instructionFrequency.RecordTakenBranch(State.LastInstructionProgramCounter, opcode, target, 4);
State.ProgramCounter = target;
CompleteTiming(M68kInstructionTimingKey.BranchWordTaken);
return;
}
Expand All @@ -4022,7 +4026,9 @@ private void ExecuteLongBranch(ushort opcode)

if (CheckCondition(condition))
{
State.ProgramCounter = unchecked((uint)(branchBase + displacement));
var target = unchecked((uint)(branchBase + displacement));
_instructionFrequency.RecordTakenBranch(State.LastInstructionProgramCounter, opcode, target, 6);
State.ProgramCounter = target;
CompleteTiming(M68kInstructionTimingKey.BranchLongTaken);
return;
}
Expand Down Expand Up @@ -4058,7 +4064,9 @@ private void ExecuteDbcc(ushort opcode)
State.D[register] = (State.D[register] & 0xFFFF_0000u) | counter;
if (counter != 0xFFFF)
{
State.ProgramCounter = unchecked((uint)(branchBase + displacement));
var target = unchecked((uint)(branchBase + displacement));
_instructionFrequency.RecordTakenBranch(State.LastInstructionProgramCounter, opcode, target, 4);
State.ProgramCounter = target;
CompleteTiming(M68kInstructionTimingKey.DbccBranchTaken);
return;
}
Expand All @@ -4070,7 +4078,7 @@ protected void BeginInstruction(ushort opcode)
{
State.LastInstructionProgramCounter = State.ProgramCounter;
State.LastOpcode = opcode;
_instructionFrequency.Record(opcode);
_instructionFrequency.Record(State.LastInstructionProgramCounter, opcode);
}

internal virtual void RaiseFormat0Exception(int vector, uint stackedProgramCounter, M68kInstructionTimingKey timingKey)
Expand Down
25 changes: 16 additions & 9 deletions Copper68k/M68kCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,10 +1082,7 @@ public int ExecuteInstruction()
var opcode = FetchWord();
State.LastOpcode = opcode;
State.LastInstructionProgramCounter = instructionPc;
if (_instructionFrequency.Enabled)
{
_instructionFrequency.Record(opcode);
}
_instructionFrequency.Record(instructionPc, opcode);

var decoded = DecodeByOpcodeLine(opcode, instructionPc);
if (decoded)
Expand Down Expand Up @@ -1283,7 +1280,9 @@ private bool DecodeBranch(ushort opcode, uint instructionPc)
var carryOrZero = (State.StatusRegister & (M68kCpuState.Carry | M68kCpuState.Zero)) != 0;
if (condition == 2 ? !carryOrZero : carryOrZero)
{
State.ProgramCounter = (uint)(branchBase + offset);
var target = (uint)(branchBase + offset);
_instructionFrequency.RecordTakenBranch(instructionPc, opcode, target, displacement == 0 ? 4 : 2);
State.ProgramCounter = target;
AddCycles(displacement == 0 ? 10 : 10);
}
else
Expand All @@ -1299,7 +1298,9 @@ private bool DecodeBranch(ushort opcode, uint instructionPc)
{
if ((State.StatusRegister & M68kCpuState.Zero) == 0)
{
State.ProgramCounter = (uint)(branchBase + offset);
var target = (uint)(branchBase + offset);
_instructionFrequency.RecordTakenBranch(instructionPc, opcode, target, displacement == 0 ? 4 : 2);
State.ProgramCounter = target;
AddCycles(displacement == 0 ? 10 : 10);
}
else
Expand All @@ -1315,7 +1316,9 @@ private bool DecodeBranch(ushort opcode, uint instructionPc)
{
if ((State.StatusRegister & M68kCpuState.Zero) != 0)
{
State.ProgramCounter = (uint)(branchBase + offset);
var target = (uint)(branchBase + offset);
_instructionFrequency.RecordTakenBranch(instructionPc, opcode, target, displacement == 0 ? 4 : 2);
State.ProgramCounter = target;
AddCycles(displacement == 0 ? 10 : 10);
}
else
Expand All @@ -1329,7 +1332,9 @@ private bool DecodeBranch(ushort opcode, uint instructionPc)

if (CheckCondition(condition))
{
State.ProgramCounter = (uint)(branchBase + offset);
var target = (uint)(branchBase + offset);
_instructionFrequency.RecordTakenBranch(instructionPc, opcode, target, displacement == 0 ? 4 : 2);
State.ProgramCounter = target;
AddCycles(displacement == 0 ? 10 : 10);
}
else
Expand Down Expand Up @@ -1847,7 +1852,9 @@ private bool DecodeLine5(ushort opcode)
State.D[reg] = (State.D[reg] & 0xFFFF_0000) | counter;
if (counter != 0xFFFF)
{
State.ProgramCounter = (uint)(branchBase + displacement);
var target = (uint)(branchBase + displacement);
_instructionFrequency.RecordTakenBranch(State.LastInstructionProgramCounter, opcode, target, 4);
State.ProgramCounter = target;
AddCycles(10);
}
else
Expand Down
134 changes: 131 additions & 3 deletions Copper68k/M68kInstructionFrequency.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,41 @@ internal readonly record struct M68kOpcodeFrequency(
string JitTargetName,
long Count);

internal readonly record struct M68kPcFrequency(
uint ProgramCounter,
ushort Opcode,
string Mnemonic,
M68kInstructionFamily Family,
string FamilyName,
M68kJitTarget JitTarget,
string JitTargetName,
long Count);

internal readonly record struct M68kHotLoopFrequency(
uint StartProgramCounter,
uint EndProgramCounter,
uint BranchProgramCounter,
uint TargetProgramCounter,
ushort BranchOpcode,
string BranchMnemonic,
int ByteLength,
long Count);

internal readonly record struct M68kInstructionFrequencySnapshot(
long TotalInstructions,
IReadOnlyList<M68kInstructionFamilyFrequency> Families,
IReadOnlyList<M68kJitTargetFrequency> JitTargets,
IReadOnlyList<M68kOpcodeFrequency> Opcodes)
IReadOnlyList<M68kOpcodeFrequency> Opcodes,
IReadOnlyList<M68kPcFrequency> HotPcs,
IReadOnlyList<M68kHotLoopFrequency> HotLoops)
{
public static M68kInstructionFrequencySnapshot Empty { get; } = new(
0,
Array.Empty<M68kInstructionFamilyFrequency>(),
Array.Empty<M68kJitTargetFrequency>(),
Array.Empty<M68kOpcodeFrequency>());
Array.Empty<M68kOpcodeFrequency>(),
Array.Empty<M68kPcFrequency>(),
Array.Empty<M68kHotLoopFrequency>());
}

internal interface IM68kInstructionFrequencyProvider
Expand All @@ -81,14 +105,20 @@ internal interface IM68kInstructionFrequencyProvider

internal sealed class M68kInstructionFrequencyMatrix
{
private const int MaxHotLoopByteLength = 4096;
private readonly long[] _familyCounts = new long[Enum.GetValues<M68kInstructionFamily>().Length];
private readonly long[] _jitTargetCounts = new long[Enum.GetValues<M68kJitTarget>().Length];
private readonly Dictionary<ushort, long> _opcodeCounts = new Dictionary<ushort, long>();
private readonly Dictionary<ulong, long> _pcCounts = new Dictionary<ulong, long>();
private readonly Dictionary<HotLoopKey, long> _hotLoopCounts = new Dictionary<HotLoopKey, long>();
private long _totalInstructions;

public bool Enabled { get; set; }

public void Record(ushort opcode)
=> Record(0, opcode);

public void Record(uint programCounter, ushort opcode)
{
if (!Enabled)
{
Expand All @@ -101,9 +131,50 @@ public void Record(ushort opcode)
_jitTargetCounts[(int)jitTarget]++;
_opcodeCounts.TryGetValue(opcode, out var count);
_opcodeCounts[opcode] = count + 1;
var pcKey = CreatePcKey(programCounter, opcode);
_pcCounts.TryGetValue(pcKey, out var pcCount);
_pcCounts[pcKey] = pcCount + 1;
_totalInstructions++;
}

public void RecordTakenBranch(
uint branchProgramCounter,
ushort branchOpcode,
uint targetProgramCounter,
int instructionByteLength)
{
if (!Enabled ||
instructionByteLength <= 0 ||
targetProgramCounter > branchProgramCounter)
{
return;
}

var endProgramCounter = unchecked(branchProgramCounter + (uint)instructionByteLength);
if (endProgramCounter < branchProgramCounter ||
targetProgramCounter >= endProgramCounter)
{
return;
}

var byteLength = endProgramCounter - targetProgramCounter;
if (byteLength == 0 ||
byteLength > MaxHotLoopByteLength)
{
return;
}

var key = new HotLoopKey(
targetProgramCounter,
endProgramCounter,
branchProgramCounter,
targetProgramCounter,
branchOpcode,
(int)byteLength);
_hotLoopCounts.TryGetValue(key, out var count);
_hotLoopCounts[key] = count + 1;
}

public M68kInstructionFrequencySnapshot CaptureSnapshot()
{
if (_totalInstructions == 0)
Expand Down Expand Up @@ -150,16 +221,73 @@ public M68kInstructionFrequencySnapshot CaptureSnapshot()
.ThenBy(entry => entry.Opcode)
.ToArray();

return new M68kInstructionFrequencySnapshot(_totalInstructions, families, jitTargets, opcodes);
var hotPcs = _pcCounts
.Select(entry =>
{
var programCounter = GetPcFromKey(entry.Key);
var opcode = GetOpcodeFromKey(entry.Key);
var family = M68kInstructionClassifier.GetFamily(opcode);
var jitTarget = M68kInstructionClassifier.GetJitTarget(opcode);
return new M68kPcFrequency(
programCounter,
opcode,
M68kInstructionClassifier.GetMnemonic(opcode),
family,
M68kInstructionClassifier.GetFamilyName(family),
jitTarget,
M68kInstructionClassifier.GetJitTargetName(jitTarget),
entry.Value);
})
.OrderByDescending(entry => entry.Count)
.ThenBy(entry => entry.ProgramCounter)
.ThenBy(entry => entry.Opcode)
.ToArray();

var hotLoops = _hotLoopCounts
.Select(entry => new M68kHotLoopFrequency(
entry.Key.StartProgramCounter,
entry.Key.EndProgramCounter,
entry.Key.BranchProgramCounter,
entry.Key.TargetProgramCounter,
entry.Key.BranchOpcode,
M68kInstructionClassifier.GetMnemonic(entry.Key.BranchOpcode),
entry.Key.ByteLength,
entry.Value))
.OrderByDescending(entry => entry.Count)
.ThenBy(entry => entry.StartProgramCounter)
.ThenBy(entry => entry.BranchProgramCounter)
.ThenBy(entry => entry.BranchOpcode)
.ToArray();

return new M68kInstructionFrequencySnapshot(_totalInstructions, families, jitTargets, opcodes, hotPcs, hotLoops);
}

public void Reset()
{
Array.Clear(_familyCounts);
Array.Clear(_jitTargetCounts);
_opcodeCounts.Clear();
_pcCounts.Clear();
_hotLoopCounts.Clear();
_totalInstructions = 0;
}

private static ulong CreatePcKey(uint programCounter, ushort opcode)
=> ((ulong)programCounter << 16) | opcode;

private static uint GetPcFromKey(ulong key)
=> (uint)(key >> 16);

private static ushort GetOpcodeFromKey(ulong key)
=> (ushort)key;

private readonly record struct HotLoopKey(
uint StartProgramCounter,
uint EndProgramCounter,
uint BranchProgramCounter,
uint TargetProgramCounter,
ushort BranchOpcode,
int ByteLength);
}

internal static class M68kInstructionClassifier
Expand Down
2 changes: 1 addition & 1 deletion Copper68k/M68kJitCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7788,7 +7788,7 @@ private bool BeginCompiledInstruction(
State.LastOpcode = expectedOpcode;
State.LastInstructionProgramCounter = programCounter;
State.ProgramCounter = Normalize(nextProgramCounter);
_instructionFrequency.Record(expectedOpcode);
_instructionFrequency.Record(programCounter, expectedOpcode);
_compiledInstructionCycleFloorActive = true;
return true;
}
Expand Down
Loading
Loading