Use two-way mapping for mnemonics -> opcodes

This commit is contained in:
Yoshi Askharoun
2024-01-31 16:58:28 -06:00
parent d77d2953b8
commit 0a205c2fa8
6 changed files with 238 additions and 85 deletions
+10 -10
View File
@@ -64,22 +64,22 @@ public class Disassembler
{ {
case OpCode.ConstructObject: case OpCode.ConstructObject:
// COBJ <typeIndex> // COBJ <typeIndex>
yield return new Instruction("COBJ", [new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16())]);
break; break;
case OpCode.ConstructObjectIndirect: case OpCode.ConstructObjectIndirect:
// COBI <assignmentTypeIndex> // COBI <assignmentTypeIndex>
yield return new Instruction("COBI", [new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16())]);
break; break;
case OpCode.ConstructObjectParam: case OpCode.ConstructObjectParam:
// COBP <targetTypeIndex> <constructorIndex> // COBP <targetTypeIndex> <constructorIndex>
yield return new Instruction("COBP", [new(reader.ReadUInt16()), new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16()), new(reader.ReadUInt16())]);
break; break;
case OpCode.ConstructFromString: case OpCode.ConstructFromString:
// CSTR <typeIndex> <stringIndex> // CSTR <typeIndex> <stringIndex>
yield return new Instruction("CSTR", [new(reader.ReadUInt16()), new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16()), new(reader.ReadUInt16())]);
break; break;
case OpCode.ConstructFromBinary: case OpCode.ConstructFromBinary:
@@ -88,38 +88,38 @@ public class Disassembler
TypeSchema cbinTypeSchema = _loadResult.ImportTables.TypeImports[cbinTypeIndex]; TypeSchema cbinTypeSchema = _loadResult.ImportTables.TypeImports[cbinTypeIndex];
object cbinObject = cbinTypeSchema.DecodeBinary(reader); object cbinObject = cbinTypeSchema.DecodeBinary(reader);
yield return new Instruction("CBIN", [new(cbinTypeIndex), new(cbinObject)]); yield return new Instruction(opCode, [new(cbinTypeIndex), new(cbinObject)]);
break; break;
// TODO // TODO
case OpCode.PropertyInitialize: case OpCode.PropertyInitialize:
// PINI <propertyIndex> // PINI <propertyIndex>
yield return new Instruction("PINI", [new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16())]);
break; break;
case OpCode.PropertyInitializeIndirect: case OpCode.PropertyInitializeIndirect:
// PINII <propertyIndex> // PINII <propertyIndex>
yield return new Instruction("PINII", [new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16())]);
break; break;
// TODO // TODO
case OpCode.PushConstant: case OpCode.PushConstant:
// PSHC <constantIndex> // PSHC <constantIndex>
yield return new Instruction("PSHC", [new(reader.ReadUInt16())]); yield return new Instruction(opCode, [new(reader.ReadUInt16())]);
break; break;
// TODO // TODO
case OpCode.ReturnValue: case OpCode.ReturnValue:
// RET // RET
yield return new Instruction("RET", []); yield return new Instruction(opCode, []);
break; break;
case OpCode.ReturnVoid: case OpCode.ReturnVoid:
// RETV // RETV
yield return new Instruction("RETV", []); yield return new Instruction(opCode, []);
break; break;
} }
} }
+80
View File
@@ -0,0 +1,80 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Iris.Asm;
/// <summary>
/// Represents a dictionary where entries can be quickly retrieved by their key or value.
/// Optimal for one-to-one mappings.
/// </summary>
public class DoubleDictionary<TLeft, TRight>(int capacity = 0) : ICollection<(TLeft, TRight)>
{
private readonly Dictionary<TLeft, TRight> _leftDict = new(capacity);
private readonly Dictionary<TRight, TLeft> _rightDict = new(capacity);
public int Count => _leftDict.Count;
public bool IsReadOnly => false;
public IReadOnlyDictionary<TLeft, TRight> GetLeftDictionary() => _leftDict;
public IReadOnlyDictionary<TRight, TLeft> GetRightDictionary() => _rightDict;
public void Add(TLeft l, TRight r)
{
_leftDict[l] = r;
_rightDict[r] = l;
}
public TRight this[TLeft l] => _leftDict[l];
public TLeft this[TRight r] => _rightDict[r];
public bool Contains(TLeft l) => _leftDict.ContainsKey(l);
public bool Contains(TRight r) => _rightDict.ContainsKey(r);
public bool TryGetRight(TLeft l, out TRight r) => _leftDict.TryGetValue(l, out r);
public bool TryGetLeft(TRight r, out TLeft l) => _rightDict.TryGetValue(r, out l);
public bool Remove(TLeft l)
{
if (!TryGetRight(l, out var r))
return false;
_leftDict.Remove(l);
return _rightDict.Remove(r);
}
public bool Remove(TRight r)
{
if (!TryGetLeft(r, out var l))
return false;
_rightDict.Remove(r);
return _leftDict.Remove(l);
}
public IEnumerator<(TLeft, TRight)> GetEnumerator() => _leftDict
.Select(kv => (kv.Key, kv.Value))
.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Add((TLeft, TRight) item) => Add(item.Item1, item.Item2);
public void Clear()
{
_leftDict.Clear();
_rightDict.Clear();
}
public bool Contains((TLeft, TRight) item)
=> _leftDict.TryGetValue(item.Item1, out var r) && r!.Equals(item.Item2);
public void CopyTo((TLeft, TRight)[] array, int arrayIndex)
{
var i = arrayIndex;
foreach (var pair in this)
array[i++] = pair;
}
public bool Remove((TLeft, TRight) item) => Remove(item.Item1) && Remove(item.Item2);
}
+5
View File
@@ -13,6 +13,11 @@ public static class Lexer
public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator); public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator);
public static readonly Parser<string> SectionDirective =
from _ in Parse.String(".section").Token()
from sectionId in Parse.Letter.AtLeastOnce().Text()
select sectionId;
public static readonly Parser<IImport> Import = ParseImport; public static readonly Parser<IImport> Import = ParseImport;
public static readonly Parser<IBodyItem> BodyItem = ParseBodyItem; public static readonly Parser<IBodyItem> BodyItem = ParseBodyItem;
+83 -74
View File
@@ -1,5 +1,4 @@
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using System.Collections.Generic;
namespace Microsoft.Iris.Asm; namespace Microsoft.Iris.Asm;
@@ -8,90 +7,100 @@ internal static class LexerMaps
public static OperationType OperationMnemonicToType(string mnemonic) => OperationMnemonicMap[mnemonic.ToUpperInvariant()]; public static OperationType OperationMnemonicToType(string mnemonic) => OperationMnemonicMap[mnemonic.ToUpperInvariant()];
public static OperationType? TryOperationMnemonicToType(string mnemonic) public static OperationType? TryOperationMnemonicToType(string mnemonic)
{ {
return OperationMnemonicMap.TryGetValue(mnemonic.ToUpperInvariant(), out var type) return OperationMnemonicMap.TryGetRight(mnemonic.ToUpperInvariant(), out var type)
? type : null; ? type : null;
} }
public static OpCode MnemonicToOpCode(string mnemonic) public static OpCode MnemonicToOpCode(string mnemonic)
{ {
if (MnemonicMap.TryGetValue(mnemonic, out var opCode)) if (MnemonicMap.TryGetRight(mnemonic, out var opCode))
return opCode; return opCode;
else if (OperationMnemonicMap.TryGetValue(mnemonic, out _)) else if (OperationMnemonicMap.TryGetRight(mnemonic, out _))
return OpCode.Operation; return OpCode.Operation;
throw new System.ArgumentException($"'{mnemonic}' is not a known UIXA instruction.", nameof(mnemonic)); throw new System.ArgumentException($"'{mnemonic}' is not a known UIXA instruction.", nameof(mnemonic));
} }
internal static readonly IDictionary<string, OperationType> OperationMnemonicMap = new Dictionary<string, OperationType> public static string GetMnemonic(OpCode opCode, OperationType? opType = null)
{ {
["ADD"] = OperationType.MathAdd, if (MnemonicMap.TryGetLeft(opCode, out var mnemonic))
["SUB"] = OperationType.MathSubtract, return mnemonic;
["MUL"] = OperationType.MathMultiply, else if (opType.HasValue && OperationMnemonicMap.TryGetLeft(opType.Value, out var operationMnemonic))
["DIV"] = OperationType.MathDivide, return operationMnemonic;
["MOD"] = OperationType.MathModulus,
["NEG"] = OperationType.MathModulus,
["AND"] = OperationType.LogicalAnd,
["ORR"] = OperationType.LogicalOr,
["NOT"] = OperationType.LogicalNot,
["REQ"] = OperationType.RelationalEquals,
["RNE"] = OperationType.RelationalNotEquals,
["RLT"] = OperationType.RelationalLessThan,
["RGT"] = OperationType.RelationalGreaterThan,
["RLE"] = OperationType.RelationalLessThanEquals,
["RGE"] = OperationType.RelationalGreaterThanEquals,
["RIS"] = OperationType.RelationalIs,
["INC"] = OperationType.PostIncrement,
["DEC"] = OperationType.PostDecrement,
};
internal static readonly IDictionary<string, OpCode> MnemonicMap = new Dictionary<string, OpCode> throw new System.ArgumentException($"'{opCode}' is not a known UIXA instruction.", nameof(opCode));
{ }
["COBJ"] = OpCode.ConstructObject,
["COBI"] = OpCode.ConstructObjectIndirect, internal static readonly DoubleDictionary<string, OperationType> OperationMnemonicMap =
["COBP"] = OpCode.ConstructObjectParam, [
["CSTR"] = OpCode.ConstructFromString, ("ADD", OperationType.MathAdd),
["CBIN"] = OpCode.ConstructFromBinary, ("SUB", OperationType.MathSubtract),
["INIT"] = OpCode.InitializeInstance, ("MUL", OperationType.MathMultiply),
["INID"] = OpCode.InitializeInstanceIndirect, ("DIV", OperationType.MathDivide),
["LSYM"] = OpCode.LookupSymbol, ("MOD", OperationType.MathModulus),
["WSYM"] = OpCode.WriteSymbol, ("NEG", OperationType.MathModulus),
["WSYP"] = OpCode.WriteSymbolPeek, ("AND", OperationType.LogicalAnd),
["CSYM"] = OpCode.ClearSymbol, ("ORR", OperationType.LogicalOr),
["PINI"] = OpCode.PropertyInitialize, ("NOT", OperationType.LogicalNot),
["PINII"] = OpCode.PropertyInitializeIndirect, ("REQ", OperationType.RelationalEquals),
["PLAD"] = OpCode.PropertyListAdd, ("RNE", OperationType.RelationalNotEquals),
["PDAD"] = OpCode.PropertyDictionaryAdd, ("RLT", OperationType.RelationalLessThan),
["PASS"] = OpCode.PropertyAssign, ("RGT", OperationType.RelationalGreaterThan),
["PASST"] = OpCode.PropertyAssignStatic, ("RLE", OperationType.RelationalLessThanEquals),
["PGET"] = OpCode.PropertyGet, ("RGE", OperationType.RelationalGreaterThanEquals),
["PGETP"] = OpCode.PropertyGetPeek, ("RIS", OperationType.RelationalIs),
["PGETT"] = OpCode.PropertyGetStatic, ("INC", OperationType.PostIncrement),
["MINV"] = OpCode.MethodInvoke, ("DEC", OperationType.PostDecrement),
["MINVP"] = OpCode.MethodInvokePeek, ];
["MINVT"] = OpCode.MethodInvokeStatic,
["MINVA"] = OpCode.MethodInvokePushLastParam, internal static readonly DoubleDictionary<string, OpCode> MnemonicMap =
["MINVAT"] = OpCode.MethodInvokeStaticPushLastParam, // Avoid using "LT" as suffix [
["VTC"] = OpCode.VerifyTypeCast, ("COBJ", OpCode.ConstructObject),
["CON"] = OpCode.ConvertType, ("COBI", OpCode.ConstructObjectIndirect),
["OPR"] = OpCode.Operation, // Generic operation, allow dynamic invocations of operators ("COBP", OpCode.ConstructObjectParam),
["ISC"] = OpCode.IsCheck, ("CSTR", OpCode.ConstructFromString),
["ASC"] = OpCode.As, ("CBIN", OpCode.ConstructFromBinary),
["TYP"] = OpCode.TypeOf, ("INIT", OpCode.InitializeInstance),
["PSHN"] = OpCode.PushNull, ("INID", OpCode.InitializeInstanceIndirect),
["PSHC"] = OpCode.PushConstant, ("LSYM", OpCode.LookupSymbol),
["PSHT"] = OpCode.PushThis, ("WSYM", OpCode.WriteSymbol),
["DIS"] = OpCode.DiscardValue, ("WSYP", OpCode.WriteSymbolPeek),
["RET"] = OpCode.ReturnValue, ("CSYM", OpCode.ClearSymbol),
["RETV"] = OpCode.ReturnVoid, ("PINI", OpCode.PropertyInitialize),
["JMPF"] = OpCode.JumpIfFalse, ("PINII", OpCode.PropertyInitializeIndirect),
["JMPFP"] = OpCode.JumpIfFalsePeek, ("PLAD", OpCode.PropertyListAdd),
["JMPTP"] = OpCode.JumpIfTruePeek, ("PDAD", OpCode.PropertyDictionaryAdd),
["JMPD"] = OpCode.JumpIfDictionaryContains, ("PASS", OpCode.PropertyAssign),
["JMPNP"] = OpCode.JumpIfNullPeek, ("PASST", OpCode.PropertyAssignStatic),
["JMP"] = OpCode.Jump, ("PGET", OpCode.PropertyGet),
["CLIS"] = OpCode.ConstructListenerStorage, ("PGETP", OpCode.PropertyGetPeek),
["LIS"] = OpCode.Listen, ("PGETT", OpCode.PropertyGetStatic),
["DLS"] = OpCode.DestructiveListen, ("MINV", OpCode.MethodInvoke),
["DBG"] = OpCode.EnterDebugState, ("MINVP", OpCode.MethodInvokePeek),
}; ("MINVT", OpCode.MethodInvokeStatic),
("MINVA", OpCode.MethodInvokePushLastParam),
("MINVAT", OpCode.MethodInvokeStaticPushLastParam), // Avoid using "LT" as suffix
("VTC", OpCode.VerifyTypeCast),
("CON", OpCode.ConvertType),
("OPR", OpCode.Operation), // Generic operation, allow dynamic invocations of operators
("ISC", OpCode.IsCheck),
("ASC", OpCode.As),
("TYP", OpCode.TypeOf),
("PSHN", OpCode.PushNull),
("PSHC", OpCode.PushConstant),
("PSHT", OpCode.PushThis),
("DIS", OpCode.DiscardValue),
("RET", OpCode.ReturnValue),
("RETV", OpCode.ReturnVoid),
("JMPF", OpCode.JumpIfFalse),
("JMPFP", OpCode.JumpIfFalsePeek),
("JMPTP", OpCode.JumpIfTruePeek),
("JMPD", OpCode.JumpIfDictionaryContains),
("JMPNP", OpCode.JumpIfNullPeek),
("JMP", OpCode.Jump),
("CLIS", OpCode.ConstructListenerStorage),
("LIS", OpCode.Listen),
("DLS", OpCode.DestructiveListen),
("DBG", OpCode.EnterDebugState),
];
} }
+19 -1
View File
@@ -1,6 +1,7 @@
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace Microsoft.Iris.Asm.Models; namespace Microsoft.Iris.Asm.Models;
@@ -9,6 +10,15 @@ public interface IImport { }
public record Instruction(string Mnemonic, IEnumerable<Operand> Operands) : IBodyItem public record Instruction(string Mnemonic, IEnumerable<Operand> Operands) : IBodyItem
{ {
public Instruction(OpCode opCode, OperationType? operationType, IEnumerable<Operand> Operands)
: this(LexerMaps.GetMnemonic(opCode, operationType), Operands)
{
}
public Instruction(OpCode opCode, IEnumerable<Operand> Operands)
: this(opCode, null, Operands)
{
}
public OpCode OpCode => LexerMaps.MnemonicToOpCode(Mnemonic); public OpCode OpCode => LexerMaps.MnemonicToOpCode(Mnemonic);
public OperationType? OperationType => LexerMaps.TryOperationMnemonicToType(Mnemonic); public OperationType? OperationType => LexerMaps.TryOperationMnemonicToType(Mnemonic);
@@ -27,5 +37,13 @@ public record Operand(object Value, string Content = null)
public record Program(IEnumerable<IImport> Imports, IEnumerable<IBodyItem> Body) public record Program(IEnumerable<IImport> Imports, IEnumerable<IBodyItem> Body)
{ {
public override string ToString() => string.Join("\r\n", Imports.Select(i => i.ToString()).Concat(Body.Select(b => b.ToString()))); public override string ToString()
{
StringBuilder sb = new();
sb.AppendJoin("\r\n", Imports.Select(i => i.ToString()));
sb.AppendJoin("\r\n", Body.Select(b => b.ToString()));
return sb.ToString();
}
} }
+41
View File
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Iris.Asm;
internal static class StringBuilderExtensions
{
#if NETSTANDARD
public static StringBuilder AppendJoin<T>(this StringBuilder sb, string? separator, IEnumerable<T> values)
{
_ = values ?? throw new ArgumentNullException(nameof(values));
separator ??= string.Empty;
using (IEnumerator<T> en = values.GetEnumerator())
{
if (!en.MoveNext())
{
return sb;
}
T value = en.Current;
if (value != null)
{
sb.Append(value.ToString());
}
while (en.MoveNext())
{
sb.Append(separator);
value = en.Current;
if (value != null)
{
sb.Append(value.ToString());
}
}
}
return sb;
}
#endif
}