From 4352bfa21893bd91836acb28937dd3874f7f2a1c Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:19:14 +0100 Subject: [PATCH] Split binary ops into another file (#71) --- WoofWare.PawPrint/BinaryArithmetic.fs | 50 +++++++ .../EvalStackValueComparisons.fs | 71 +++++++++ WoofWare.PawPrint/NullaryIlOp.fs | 140 +----------------- WoofWare.PawPrint/WoofWare.PawPrint.fsproj | 2 + 4 files changed, 131 insertions(+), 132 deletions(-) create mode 100644 WoofWare.PawPrint/BinaryArithmetic.fs create mode 100644 WoofWare.PawPrint/EvalStackValueComparisons.fs diff --git a/WoofWare.PawPrint/BinaryArithmetic.fs b/WoofWare.PawPrint/BinaryArithmetic.fs new file mode 100644 index 0000000..0d0e390 --- /dev/null +++ b/WoofWare.PawPrint/BinaryArithmetic.fs @@ -0,0 +1,50 @@ +namespace WoofWare.PawPrint + +#nowarn "42" + +type IArithmeticOperation = + abstract Int32Int32 : int32 -> int32 -> int32 + abstract Int64Int64 : int64 -> int64 -> int64 + abstract FloatFloat : float -> float -> float + abstract Name : string + +[] +module ArithmeticOperation = + let add = + { new IArithmeticOperation with + member _.Int32Int32 a b = (# "add" a b : int32 #) + member _.Int64Int64 a b = (# "add" a b : int64 #) + member _.FloatFloat a b = (# "add" a b : float #) + member _.Name = "add" + } + + let mul = + { new IArithmeticOperation with + member _.Int32Int32 a b = (# "mul" a b : int32 #) + member _.Int64Int64 a b = (# "mul" a b : int64 #) + member _.FloatFloat a b = (# "mul" a b : float #) + member _.Name = "mul" + } + +[] +module BinaryArithmetic = + let execute (op : IArithmeticOperation) (val1 : EvalStackValue) (val2 : EvalStackValue) : EvalStackValue = + // see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0 + match val1, val2 with + | EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 -> op.Int32Int32 val1 val2 |> EvalStackValue.Int32 + | EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt + | EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 -> failwith "" |> EvalStackValue.ManagedPointer + | EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef + | EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 -> op.Int64Int64 val1 val2 |> EvalStackValue.Int64 + | EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.NativeInt + | EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt + | EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 -> + failwith "" |> EvalStackValue.ManagedPointer + | EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef + | EvalStackValue.Float val1, EvalStackValue.Float val2 -> op.FloatFloat val1 val2 |> EvalStackValue.Float + | EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 -> + failwith "" |> EvalStackValue.ManagedPointer + | EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.ObjectRef + | EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ManagedPointer + | EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef + | val1, val2 -> failwith $"invalid %s{op.Name} operation: {val1} and {val2}" diff --git a/WoofWare.PawPrint/EvalStackValueComparisons.fs b/WoofWare.PawPrint/EvalStackValueComparisons.fs new file mode 100644 index 0000000..188dfdc --- /dev/null +++ b/WoofWare.PawPrint/EvalStackValueComparisons.fs @@ -0,0 +1,71 @@ +namespace WoofWare.PawPrint + +[] +module EvalStackValueComparisons = + + let clt (var1 : EvalStackValue) (var2 : EvalStackValue) : bool = + match var1, var2 with + | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> var1 < var2 + | EvalStackValue.Float var1, EvalStackValue.Float var2 -> failwith "TODO: Clt float comparison unimplemented" + | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> + failwith $"Clt instruction invalid for comparing object refs, {var1} vs {var2}" + | EvalStackValue.ObjectRef var1, other -> failwith $"invalid comparison, ref %O{var1} vs %O{other}" + | other, EvalStackValue.ObjectRef var2 -> failwith $"invalid comparison, %O{other} vs ref %O{var2}" + | EvalStackValue.Float i, other -> failwith $"invalid comparison, float %f{i} vs %O{other}" + | other, EvalStackValue.Float i -> failwith $"invalid comparison, %O{other} vs float %f{i}" + | EvalStackValue.Int64 i, other -> failwith $"invalid comparison, int64 %i{i} vs %O{other}" + | other, EvalStackValue.Int64 i -> failwith $"invalid comparison, %O{other} vs int64 %i{i}" + | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> var1 < var2 + | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> + failwith "TODO: Clt Int32 vs NativeInt comparison unimplemented" + | EvalStackValue.Int32 i, other -> failwith $"invalid comparison, int32 %i{i} vs %O{other}" + | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 -> + failwith "TODO: Clt NativeInt vs Int32 comparison unimplemented" + | other, EvalStackValue.Int32 var2 -> failwith $"invalid comparison, {other} vs int32 {var2}" + | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> NativeIntSource.isLess var1 var2 + | EvalStackValue.NativeInt var1, other -> failwith $"invalid comparison, nativeint {var1} vs %O{other}" + | EvalStackValue.ManagedPointer managedPointerSource, NativeInt int64 -> + failwith "TODO: Clt ManagedPointer vs NativeInt comparison unimplemented" + | EvalStackValue.ManagedPointer managedPointerSource, ManagedPointer pointerSource -> + failwith "TODO: Clt ManagedPointer vs ManagedPointer comparison unimplemented" + | EvalStackValue.ManagedPointer managedPointerSource, UserDefinedValueType _ -> + failwith "TODO: Clt ManagedPointer vs UserDefinedValueType comparison unimplemented" + | EvalStackValue.UserDefinedValueType _, NativeInt int64 -> + failwith "TODO: Clt UserDefinedValueType vs NativeInt comparison unimplemented" + | EvalStackValue.UserDefinedValueType _, ManagedPointer managedPointerSource -> + failwith "TODO: Clt UserDefinedValueType vs ManagedPointer comparison unimplemented" + | EvalStackValue.UserDefinedValueType _, UserDefinedValueType _ -> + failwith "TODO: Clt UserDefinedValueType vs UserDefinedValueType comparison unimplemented" + + let ceq var1 var2 : bool = + // Table III.4 + match var1, var2 with + | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> var1 = var2 + | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> failwith "TODO: int32 CEQ nativeint" + | EvalStackValue.Int32 _, _ -> failwith $"bad ceq: Int32 vs {var2}" + | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> var1 = var2 + | EvalStackValue.Int64 _, _ -> failwith $"bad ceq: Int64 vs {var2}" + | EvalStackValue.Float var1, EvalStackValue.Float var2 -> failwith "TODO: float CEQ float" + | EvalStackValue.Float _, _ -> failwith $"bad ceq: Float vs {var2}" + | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> + match var1, var2 with + | NativeIntSource.FunctionPointer f1, NativeIntSource.FunctionPointer f2 -> + if f1 = f2 then + true + else + failwith $"TODO(CEQ): nativeint vs nativeint, {f1} vs {f2}" + | NativeIntSource.TypeHandlePtr f1, NativeIntSource.TypeHandlePtr f2 -> f1 = f2 + | NativeIntSource.Verbatim f1, NativeIntSource.Verbatim f2 -> f1 = f2 + | NativeIntSource.ManagedPointer f1, NativeIntSource.ManagedPointer f2 -> f1 = f2 + | _, _ -> failwith $"TODO (CEQ): nativeint vs nativeint, {var1} vs {var2}" + | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 -> failwith $"TODO (CEQ): nativeint vs int32" + | EvalStackValue.NativeInt var1, EvalStackValue.ManagedPointer var2 -> + failwith $"TODO (CEQ): nativeint vs managed pointer" + | EvalStackValue.NativeInt _, _ -> failwith $"bad ceq: NativeInt vs {var2}" + | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> var1 = var2 + | EvalStackValue.ObjectRef _, _ -> failwith $"bad ceq: ObjectRef vs {var2}" + | EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> var1 = var2 + | EvalStackValue.ManagedPointer var1, EvalStackValue.NativeInt var2 -> + failwith $"TODO (CEQ): managed pointer vs nativeint" + | EvalStackValue.ManagedPointer _, _ -> failwith $"bad ceq: ManagedPointer vs {var2}" + | EvalStackValue.UserDefinedValueType _, _ -> failwith $"bad ceq: UserDefinedValueType vs {var2}" diff --git a/WoofWare.PawPrint/NullaryIlOp.fs b/WoofWare.PawPrint/NullaryIlOp.fs index c6c01bc..847ef5b 100644 --- a/WoofWare.PawPrint/NullaryIlOp.fs +++ b/WoofWare.PawPrint/NullaryIlOp.fs @@ -1,33 +1,7 @@ namespace WoofWare.PawPrint -#nowarn "42" - open Microsoft.Extensions.Logging -type private IArithmeticOperation = - abstract Int32Int32 : int32 -> int32 -> int32 - abstract Int64Int64 : int64 -> int64 -> int64 - abstract FloatFloat : float -> float -> float - abstract Name : string - -[] -module private ArithmeticOperation = - let add = - { new IArithmeticOperation with - member _.Int32Int32 a b = (# "add" a b : int32 #) - member _.Int64Int64 a b = (# "add" a b : int64 #) - member _.FloatFloat a b = (# "add" a b : float #) - member _.Name = "add" - } - - let mul = - { new IArithmeticOperation with - member _.Int32Int32 a b = (# "mul" a b : int32 #) - member _.Int64Int64 a b = (# "mul" a b : int64 #) - member _.FloatFloat a b = (# "mul" a b : float #) - member _.Name = "mul" - } - [] [] module NullaryIlOp = @@ -103,41 +77,6 @@ module NullaryIlOp = (state, WhatWeDid.Executed) |> ExecutionResult.Stepped - let private binaryArithmeticOperation - (op : IArithmeticOperation) - (currentThread : ThreadId) - (state : IlMachineState) - = - let val1, state = IlMachineState.popEvalStack currentThread state - let val2, state = IlMachineState.popEvalStack currentThread state - // see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0 - let result = - match val1, val2 with - | EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 -> - (# "add" val1 val2 : int32 #) |> EvalStackValue.Int32 - | EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt - | EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 -> - failwith "" |> EvalStackValue.ManagedPointer - | EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef - | EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 -> - (# "add" val1 val2 : int64 #) |> EvalStackValue.Int64 - | EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.NativeInt - | EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt - | EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 -> - failwith "" |> EvalStackValue.ManagedPointer - | EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef - | EvalStackValue.Float val1, EvalStackValue.Float val2 -> - (# "add" val1 val2 : float #) |> EvalStackValue.Float - | EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 -> - failwith "" |> EvalStackValue.ManagedPointer - | EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.ObjectRef - | EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 -> - failwith "" |> EvalStackValue.ManagedPointer - | EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef - | val1, val2 -> failwith $"invalid %s{op.Name} operation: {val1} and {val2}" - - result, state - let private stind (varType : CliType) (currentThread : ThreadId) (state : IlMachineState) : IlMachineState = // TODO: throw NullReferenceException if unaligned target let valueToStore, state = IlMachineState.popEvalStack currentThread state @@ -346,38 +285,7 @@ module NullaryIlOp = let var2, state = state |> IlMachineState.popEvalStack currentThread let var1, state = state |> IlMachineState.popEvalStack currentThread - let comparisonResult = - // Table III.4 - match var1, var2 with - | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> if var1 = var2 then 1 else 0 - | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> failwith "TODO: int32 CEQ nativeint" - | EvalStackValue.Int32 _, _ -> failwith $"bad ceq: Int32 vs {var2}" - | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> if var1 = var2 then 1 else 0 - | EvalStackValue.Int64 _, _ -> failwith $"bad ceq: Int64 vs {var2}" - | EvalStackValue.Float var1, EvalStackValue.Float var2 -> failwith "TODO: float CEQ float" - | EvalStackValue.Float _, _ -> failwith $"bad ceq: Float vs {var2}" - | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> - match var1, var2 with - | NativeIntSource.FunctionPointer f1, NativeIntSource.FunctionPointer f2 -> - if f1 = f2 then - 1 - else - failwith $"TODO(CEQ): nativeint vs nativeint, {f1} vs {f2}" - | NativeIntSource.TypeHandlePtr f1, NativeIntSource.TypeHandlePtr f2 -> if f1 = f2 then 1 else 0 - | NativeIntSource.Verbatim f1, NativeIntSource.Verbatim f2 -> if f1 = f2 then 1 else 0 - | NativeIntSource.ManagedPointer f1, NativeIntSource.ManagedPointer f2 -> if f1 = f2 then 1 else 0 - | _, _ -> failwith $"TODO (CEQ): nativeint vs nativeint, {var1} vs {var2}" - | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 -> failwith $"TODO (CEQ): nativeint vs int32" - | EvalStackValue.NativeInt var1, EvalStackValue.ManagedPointer var2 -> - failwith $"TODO (CEQ): nativeint vs managed pointer" - | EvalStackValue.NativeInt _, _ -> failwith $"bad ceq: NativeInt vs {var2}" - | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> if var1 = var2 then 1 else 0 - | EvalStackValue.ObjectRef _, _ -> failwith $"bad ceq: ObjectRef vs {var2}" - | EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> if var1 = var2 then 1 else 0 - | EvalStackValue.ManagedPointer var1, EvalStackValue.NativeInt var2 -> - failwith $"TODO (CEQ): managed pointer vs nativeint" - | EvalStackValue.ManagedPointer _, _ -> failwith $"bad ceq: ManagedPointer vs {var2}" - | EvalStackValue.UserDefinedValueType _, _ -> failwith $"bad ceq: UserDefinedValueType vs {var2}" + let comparisonResult = if EvalStackValueComparisons.ceq var1 var2 then 1 else 0 state |> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 comparisonResult) currentThread @@ -390,41 +298,7 @@ module NullaryIlOp = let var2, state = state |> IlMachineState.popEvalStack currentThread let var1, state = state |> IlMachineState.popEvalStack currentThread - let comparisonResult = - match var1, var2 with - | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> if var1 < var2 then 1 else 0 - | EvalStackValue.Float var1, EvalStackValue.Float var2 -> - failwith "TODO: Clt float comparison unimplemented" - | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> - failwith $"Clt instruction invalid for comparing object refs, {var1} vs {var2}" - | EvalStackValue.ObjectRef var1, other -> failwith $"invalid comparison, ref %O{var1} vs %O{other}" - | other, EvalStackValue.ObjectRef var2 -> failwith $"invalid comparison, %O{other} vs ref %O{var2}" - | EvalStackValue.Float i, other -> failwith $"invalid comparison, float %f{i} vs %O{other}" - | other, EvalStackValue.Float i -> failwith $"invalid comparison, %O{other} vs float %f{i}" - | EvalStackValue.Int64 i, other -> failwith $"invalid comparison, int64 %i{i} vs %O{other}" - | other, EvalStackValue.Int64 i -> failwith $"invalid comparison, %O{other} vs int64 %i{i}" - | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> if var1 < var2 then 1 else 0 - | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> - failwith "TODO: Clt Int32 vs NativeInt comparison unimplemented" - | EvalStackValue.Int32 i, other -> failwith $"invalid comparison, int32 %i{i} vs %O{other}" - | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 -> - failwith "TODO: Clt NativeInt vs Int32 comparison unimplemented" - | other, EvalStackValue.Int32 var2 -> failwith $"invalid comparison, {other} vs int32 {var2}" - | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> - if NativeIntSource.isLess var1 var2 then 1 else 0 - | EvalStackValue.NativeInt var1, other -> failwith $"invalid comparison, nativeint {var1} vs %O{other}" - | EvalStackValue.ManagedPointer managedPointerSource, NativeInt int64 -> - failwith "TODO: Clt ManagedPointer vs NativeInt comparison unimplemented" - | EvalStackValue.ManagedPointer managedPointerSource, ManagedPointer pointerSource -> - failwith "TODO: Clt ManagedPointer vs ManagedPointer comparison unimplemented" - | EvalStackValue.ManagedPointer managedPointerSource, UserDefinedValueType _ -> - failwith "TODO: Clt ManagedPointer vs UserDefinedValueType comparison unimplemented" - | EvalStackValue.UserDefinedValueType _, NativeInt int64 -> - failwith "TODO: Clt UserDefinedValueType vs NativeInt comparison unimplemented" - | EvalStackValue.UserDefinedValueType _, ManagedPointer managedPointerSource -> - failwith "TODO: Clt UserDefinedValueType vs ManagedPointer comparison unimplemented" - | EvalStackValue.UserDefinedValueType _, UserDefinedValueType _ -> - failwith "TODO: Clt UserDefinedValueType vs UserDefinedValueType comparison unimplemented" + let comparisonResult = if EvalStackValueComparisons.clt var1 var2 then 1 else 0 state |> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 comparisonResult) currentThread @@ -460,8 +334,9 @@ module NullaryIlOp = | Sub_ovf -> failwith "TODO: Sub_ovf unimplemented" | Sub_ovf_un -> failwith "TODO: Sub_ovf_un unimplemented" | Add -> - let result, state = - binaryArithmeticOperation ArithmeticOperation.add currentThread state + let val1, state = IlMachineState.popEvalStack currentThread state + let val2, state = IlMachineState.popEvalStack currentThread state + let result = BinaryArithmetic.execute ArithmeticOperation.add val1 val2 state |> IlMachineState.pushToEvalStack' result currentThread @@ -471,8 +346,9 @@ module NullaryIlOp = | Add_ovf -> failwith "TODO: Add_ovf unimplemented" | Add_ovf_un -> failwith "TODO: Add_ovf_un unimplemented" | Mul -> - let result, state = - binaryArithmeticOperation ArithmeticOperation.mul currentThread state + let val1, state = IlMachineState.popEvalStack currentThread state + let val2, state = IlMachineState.popEvalStack currentThread state + let result = BinaryArithmetic.execute ArithmeticOperation.mul val1 val2 state |> IlMachineState.pushToEvalStack' result currentThread diff --git a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj index 3e8a007..948d72c 100644 --- a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj +++ b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj @@ -16,6 +16,8 @@ + +