Add instruction schema and some type information

This commit is contained in:
Yoshi Askharoun
2024-02-06 12:55:46 -06:00
parent 59bc842e0a
commit b4da2a46d7
4 changed files with 277 additions and 14 deletions
+16 -13
View File
@@ -62,7 +62,7 @@ public class Disassembler
// Insert a label to mark the start of the object section. // Insert a label to mark the start of the object section.
// In the future, this might use a special directive like `.section object` // In the future, this might use a special directive like `.section object`
yield return new Label("code"); yield return new SectionDirective("object");
while (reader.CurrentOffset < reader.Size) while (reader.CurrentOffset < reader.Size)
{ {
@@ -77,7 +77,7 @@ public class Disassembler
case OpCode.DiscardValue: // DIS case OpCode.DiscardValue: // DIS
case OpCode.ReturnValue: // RET case OpCode.ReturnValue: // RET
case OpCode.ReturnVoid: // RETV case OpCode.ReturnVoid: // RETV
yield return new Instruction(opCode, []); yield return Instruction.CreateWithSchema(opCode);
break; break;
// CMD <UInt16> // CMD <UInt16>
@@ -107,7 +107,7 @@ public class Disassembler
case OpCode.TypeOf: // TYP <typeIndex> case OpCode.TypeOf: // TYP <typeIndex>
case OpCode.PushConstant: // PSHC <constantIndex> case OpCode.PushConstant: // PSHC <constantIndex>
case OpCode.ConstructListenerStorage: // CLIS <listenerCount> case OpCode.ConstructListenerStorage: // CLIS <listenerCount>
yield return new Instruction(opCode, [new(reader.ReadUInt16())]); yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16());
break; break;
// CMD <UInt32> // CMD <UInt32>
@@ -116,12 +116,12 @@ public class Disassembler
case OpCode.JumpIfTruePeek: // JMPT <jumpTo> case OpCode.JumpIfTruePeek: // JMPT <jumpTo>
case OpCode.JumpIfNullPeek: // JMPNP <jumpTo> case OpCode.JumpIfNullPeek: // JMPNP <jumpTo>
case OpCode.Jump: // JMP <jumpTo> case OpCode.Jump: // JMP <jumpTo>
yield return new Instruction(opCode, [new(reader.ReadUInt32())]); yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt32());
break; break;
// CMD <Int32> // CMD <Int32>
case OpCode.EnterDebugState: // DBG <breakpointIndex> case OpCode.EnterDebugState: // DBG <breakpointIndex>
yield return new Instruction(opCode, [new(reader.ReadInt32())]); yield return Instruction.CreateWithSchema(opCode, reader.ReadInt32());
break; break;
// CMD <UInt16> <UInt16> // CMD <UInt16> <UInt16>
@@ -129,11 +129,11 @@ public class Disassembler
case OpCode.ConstructFromString: // CSTR <typeIndex> <stringIndex> case OpCode.ConstructFromString: // CSTR <typeIndex> <stringIndex>
case OpCode.PropertyDictionaryAdd: // PDAD <propertyIndex> <keyIndex> case OpCode.PropertyDictionaryAdd: // PDAD <propertyIndex> <keyIndex>
case OpCode.ConvertType: // CON <toTypeIndex> <fromTypeIndex> case OpCode.ConvertType: // CON <toTypeIndex> <fromTypeIndex>
yield return new Instruction(opCode, [new(reader.ReadUInt16()), new(reader.ReadUInt16())]); yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16(), reader.ReadUInt16());
break; break;
case OpCode.JumpIfDictionaryContains: // JMPD <propertyIndex> <keyIndex> <jumpTo> case OpCode.JumpIfDictionaryContains: // JMPD <propertyIndex> <keyIndex> <jumpTo>
yield return new Instruction(opCode, [new(reader.ReadUInt16()), new(reader.ReadUInt16()), new(reader.ReadUInt32())]); yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt32());
break; break;
case OpCode.ConstructFromBinary: // CBIN <typeIndex> <object> case OpCode.ConstructFromBinary: // CBIN <typeIndex> <object>
@@ -141,25 +141,28 @@ 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(opCode, [new(cbinTypeIndex), new(cbinObject)]); yield return Instruction.CreateWithSchema(opCode, cbinTypeIndex, cbinObject);
break; break;
case OpCode.Operation: // OPR <opHostIndex> <operation> case OpCode.Operation: // OPR <opHostIndex> <operation>
var opHostIndex = reader.ReadUInt16(); var opHostIndex = reader.ReadUInt16();
var op = (OperationType)reader.ReadByte(); var op = (OperationType)reader.ReadByte();
yield return new Instruction(opCode, op, [new(opHostIndex)]); yield return Instruction.CreateWithSchema(opCode, opHostIndex, op);
break; break;
case OpCode.Listen: // LIS <listenerIndex> <listenerType> <watchIndex> <handlerOffset> case OpCode.Listen: // LIS <listenerIndex> <listenerType> <watchIndex> <handlerOffset>
case OpCode.DestructiveListen: // LISD <listenerIndex> <listenerType> <watchIndex> <handlerOffset> <refreshOffset> case OpCode.DestructiveListen: // LISD <listenerIndex> <listenerType> <watchIndex> <handlerOffset> <refreshOffset>
List<Operand> lisOperands = [new(reader.ReadUInt16()), new(reader.ReadByte()), var listenerIndex = reader.ReadUInt16();
new(reader.ReadUInt16()), new(reader.ReadUInt32())]; var listenerType = reader.ReadByte();
var watchIndex = reader.ReadUInt16();
var handlerOffset = reader.ReadUInt32();
if (opCode == OpCode.DestructiveListen) if (opCode == OpCode.DestructiveListen)
lisOperands.Add(new(reader.ReadUInt32())); yield return Instruction.CreateWithSchema(opCode, listenerIndex, listenerType, watchIndex, handlerOffset, reader.ReadUInt32());
else
yield return Instruction.CreateWithSchema(opCode, listenerIndex, listenerType, watchIndex, handlerOffset);
yield return new Instruction(opCode, lisOperands);
break; break;
default: default:
+171
View File
@@ -0,0 +1,171 @@
using Microsoft.Iris.Asm.Models;
using Microsoft.Iris.Markup;
using System.Collections.Generic;
namespace Microsoft.Iris.Asm;
internal static class InstructionSet
{
public static OperationType OperationMnemonicToType(string mnemonic) => OperationMnemonicMap[mnemonic.ToUpperInvariant()];
public static OperationType? TryOperationMnemonicToType(string mnemonic)
{
return OperationMnemonicMap.TryGetRight(mnemonic.ToUpperInvariant(), out var type)
? type : null;
}
public static OpCode MnemonicToOpCode(string mnemonic)
{
if (MnemonicMap.TryGetRight(mnemonic, out var opCode))
return opCode;
else if (OperationMnemonicMap.TryGetRight(mnemonic, out _))
return OpCode.Operation;
throw new System.ArgumentException($"'{mnemonic}' is not a known UIXA instruction.", nameof(mnemonic));
}
public static string GetMnemonic(OpCode opCode, OperationType? opType = null)
{
if (MnemonicMap.TryGetLeft(opCode, out var mnemonic))
return mnemonic;
else if (opType.HasValue && OperationMnemonicMap.TryGetLeft(opType.Value, out var operationMnemonic))
return operationMnemonic;
throw new System.ArgumentException($"'{opCode}' is not a known UIXA instruction.", nameof(opCode));
}
internal static readonly DoubleDictionary<string, OperationType> OperationMnemonicMap =
[
("ADD", OperationType.MathAdd),
("SUB", OperationType.MathSubtract),
("MUL", OperationType.MathMultiply),
("DIV", OperationType.MathDivide),
("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 DoubleDictionary<string, OpCode> MnemonicMap =
[
("COBJ", OpCode.ConstructObject),
("COBJI", OpCode.ConstructObjectIndirect),
("COBP", OpCode.ConstructObjectParam),
("CSTR", OpCode.ConstructFromString),
("CBIN", OpCode.ConstructFromBinary),
("INIT", OpCode.InitializeInstance),
("INITI", OpCode.InitializeInstanceIndirect),
("LSYM", OpCode.LookupSymbol),
("WSYM", OpCode.WriteSymbol),
("WSYMP", OpCode.WriteSymbolPeek),
("CSYM", OpCode.ClearSymbol),
("PINI", OpCode.PropertyInitialize),
("PINII", OpCode.PropertyInitializeIndirect),
("PLAD", OpCode.PropertyListAdd),
("PDAD", OpCode.PropertyDictionaryAdd),
("PASS", OpCode.PropertyAssign),
("PASST", OpCode.PropertyAssignStatic),
("PGET", OpCode.PropertyGet),
("PGETP", OpCode.PropertyGetPeek),
("PGETT", OpCode.PropertyGetStatic),
("MINV", OpCode.MethodInvoke),
("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),
("LISD", OpCode.DestructiveListen),
("DBG", OpCode.EnterDebugState),
];
private static readonly OperandDataType[] Inst_UInt16 = [OperandDataType.UInt16];
private static readonly OperandDataType[] Inst_UInt32 = [OperandDataType.UInt32];
private static readonly OperandDataType[] Inst_UInt16x2 = [OperandDataType.UInt16, OperandDataType.UInt16];
public static readonly Dictionary<OpCode, OperandDataType[]> InstructionSchema = new()
{
[OpCode.InitializeInstanceIndirect] = [],
[OpCode.PushNull] = [],
[OpCode.PushThis] = [],
[OpCode.DiscardValue] = [],
[OpCode.ReturnValue] = [],
[OpCode.ReturnVoid] = [],
[OpCode.ConstructObject] = Inst_UInt16,
[OpCode.ConstructObjectIndirect] = Inst_UInt16,
[OpCode.InitializeInstance] = Inst_UInt16,
[OpCode.LookupSymbol] = Inst_UInt16,
[OpCode.WriteSymbol] = Inst_UInt16,
[OpCode.WriteSymbolPeek] = Inst_UInt16,
[OpCode.ClearSymbol] = Inst_UInt16,
[OpCode.PropertyInitialize] = Inst_UInt16,
[OpCode.PropertyInitializeIndirect] = Inst_UInt16,
[OpCode.PropertyListAdd] = Inst_UInt16,
[OpCode.PropertyAssign] = Inst_UInt16,
[OpCode.PropertyAssignStatic] = Inst_UInt16,
[OpCode.PropertyGet] = Inst_UInt16,
[OpCode.PropertyGetPeek] = Inst_UInt16,
[OpCode.PropertyGetStatic] = Inst_UInt16,
[OpCode.MethodInvoke] = Inst_UInt16,
[OpCode.MethodInvokePeek] = Inst_UInt16,
[OpCode.MethodInvokeStatic] = Inst_UInt16,
[OpCode.MethodInvokePushLastParam] = Inst_UInt16,
[OpCode.MethodInvokeStaticPushLastParam] = Inst_UInt16,
[OpCode.VerifyTypeCast] = Inst_UInt16,
[OpCode.IsCheck] = Inst_UInt16,
[OpCode.As] = Inst_UInt16,
[OpCode.TypeOf] = Inst_UInt16,
[OpCode.PushConstant] = Inst_UInt16,
[OpCode.ConstructListenerStorage] = Inst_UInt16,
[OpCode.JumpIfFalse] = Inst_UInt32,
[OpCode.JumpIfFalsePeek] = Inst_UInt32,
[OpCode.JumpIfTruePeek] = Inst_UInt32,
[OpCode.JumpIfNullPeek] = Inst_UInt32,
[OpCode.Jump] = Inst_UInt32,
[OpCode.EnterDebugState] = [OperandDataType.Int32],
[OpCode.ConstructObjectParam] = Inst_UInt16x2,
[OpCode.ConstructFromString] = Inst_UInt16x2,
[OpCode.PropertyDictionaryAdd] = Inst_UInt16x2,
[OpCode.ConvertType] = Inst_UInt16x2,
[OpCode.JumpIfDictionaryContains] = [OperandDataType.UInt16, OperandDataType.UInt16, OperandDataType.UInt32],
[OpCode.ConstructFromBinary] = [OperandDataType.UInt16, OperandDataType.Bytes],
[OpCode.Operation] = [OperandDataType.UInt16, OperandDataType.Byte],
[OpCode.Listen] = [OperandDataType.UInt16, OperandDataType.Byte, OperandDataType.UInt16, OperandDataType.UInt32],
[OpCode.DestructiveListen] = [OperandDataType.UInt16, OperandDataType.Byte, OperandDataType.UInt16, OperandDataType.UInt32, OperandDataType.UInt32],
};
}
+23 -1
View File
@@ -54,8 +54,30 @@ partial class Lexer
input = ConsumeWhitespace(input); input = ConsumeWhitespace(input);
var operandsResult = Parse.Ref(() => AlphanumericText).DelimitedBy(Parse.Char(',').Token())(input); var operandsResult = Parse.Ref(() => AlphanumericText).DelimitedBy(Parse.Char(',').Token())(input);
operands.AddRange(operandsResult.Value.Select(s => new Operand(s) { Line = line }));
input = operandsResult.Remainder; input = operandsResult.Remainder;
var opCode = InstructionSet.MnemonicToOpCode(identifier);
var schema = InstructionSet.InstructionSchema[opCode];
int schemaIndex = 0;
foreach (var operandContent in operandsResult.Value)
{
var operandType = schema[schemaIndex++];
object operandValue = operandType switch
{
OperandDataType.Byte => byte.Parse(operandContent),
OperandDataType.UInt16 => ushort.Parse(operandContent),
OperandDataType.UInt32 => uint.Parse(operandContent),
OperandDataType.Int32 => int.Parse(operandContent),
OperandDataType.Bytes or _ => operandContent,
};
operands.Add(new(operandValue, operandType, operandContent)
{
Line = line,
});
}
} }
bodyItem = new Instruction(identifier, operands) bodyItem = new Instruction(identifier, operands)
+67
View File
@@ -0,0 +1,67 @@
using Microsoft.Iris.Markup;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Microsoft.Iris.Asm.Models;
[DebuggerDisplay("{ToString()} " + DebuggerDisplay)]
public record Instruction(string Mnemonic, IEnumerable<Operand> Operands) : BodyItem
{
public Instruction(OpCode opCode, OperationType? operationType, IEnumerable<Operand> Operands)
: this(InstructionSet.GetMnemonic(opCode, operationType), Operands)
{
}
public Instruction(OpCode opCode, IEnumerable<Operand> Operands)
: this(opCode, null, Operands)
{
}
public OpCode OpCode => InstructionSet.MnemonicToOpCode(Mnemonic);
public OperationType? OperationType => InstructionSet.TryOperationMnemonicToType(Mnemonic);
public override string ToString() => ToString(true);
public string ToString(bool uppercase)
{
var mnemonic = uppercase ? Mnemonic.ToUpperInvariant() : Mnemonic.ToLowerInvariant();
return Operands.Any()
? $"{mnemonic} {string.Join(", ", Operands)}"
: mnemonic;
}
public static Instruction CreateParamless(OpCode opCode) => new(opCode, Array.Empty<Operand>());
public static Instruction CreateUInt16(OpCode opCode, ushort operand1)
=> new(opCode, [new(operand1, OperandDataType.UInt16)]);
public static Instruction CreateUInt32(OpCode opCode, uint operand1)
=> new(opCode, [new(operand1, OperandDataType.UInt32)]);
public static Instruction CreateInt32(OpCode opCode, int operand1)
=> new(opCode, [new(operand1, OperandDataType.Int32)]);
public static Instruction CreateUInt16UInt16(OpCode opCode, ushort operand1, ushort operand2)
=> new(opCode, [new(operand1, OperandDataType.UInt16), new(operand2, OperandDataType.UInt16)]);
public static Instruction CreateWithSchema(OpCode opCode, params object[] operands)
{
var schema = InstructionSet.InstructionSchema[opCode];
if (operands.Length != schema.Length)
throw new ArgumentException($"{opCode} requires {schema.Length} operands, got {operands.Length}");
var operandModels = new Operand[operands.Length];
for (int i = 0; i < schema.Length; i++)
{
var operandValue = operands[i];
// Should we verify types?
operandModels[i] = new(operandValue, schema[i]);
}
var operationType = operands.Length > 0 ? operands[0] as OperationType? : null;
return new Instruction(opCode, operationType, operandModels);
}
}