IFPSTools.NET/LibIFPSCC/CGen/TypeCast.cs
zc e16c00799d ifpscc: initial commit
libifpscc: initial commit
readme: document ifpscc/libifpscc
license: add credits for ifpscc/libifpscc (derived from code also MIT licensed)
libifps: make additional fields/types public for libifpscc
libifps: fix field documentation for some opcodes
libifps: fix loading functions that are not exported
libifps: allow saving a nonexistant primitive type if the same primitive type was added already
libifps: fix parsing Extended constants
libifps: fix ushort/short being mapped to the wrong types in one table
csproj: set Prefer32Bit=false for release builds
2023-03-28 17:24:19 +01:00

155 lines
7.5 KiB
C#

using System;
using CodeGeneration;
using IFPSLib.Emit;
namespace ABT {
public sealed partial class TypeCast {
public override Operand CGenValue(CGenState state, Operand retLoc)
{
if (this.Kind == TypeCastType.FUNC_TO_PTR)
{
switch (this.Expr)
{
case Variable func:
return new Operand(IFPSLib.TypedData.Create(state.EmitType(this.Type) as IFPSLib.Types.FunctionPointerType, state.GetFunction(func.Name)));
default:
throw new InvalidProgramException().Attach(Expr);
}
}
if (this.Kind == TypeCastType.NOP) return Expr.CGenValue(state, retLoc);
var ret = this.Expr.CGenValue(state, null);
Operand op = null;
PointerType ptrType = null;
Operand dummyForType = null;
switch (this.Kind) {
case TypeCastType.ARRAY_TO_PTR:
// easy, pushvar
return state.FunctionState.PushVar(ret);
case TypeCastType.PTR_TO_INT32:
op = state.FunctionState.PushType(state.EmitType(Type));
state.CGenPushStackSize();
state.FunctionState.PushVar(ret);
state.FunctionState.PushVar(op);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
state.CGenPopStackSize();
return op;
case TypeCastType.INT32_TO_PTR:
op = state.FunctionState.PushType(state.TypeArrayOfPointer);
ptrType = Type as PointerType;
if (ptrType == null) throw new InvalidProgramException().Attach(Expr);
state.CGenPushStackSize();
dummyForType = state.FunctionState.PushType(state.EmitType(ptrType.RefType));
state.FunctionState.PushVar(op);
state.FunctionState.Push(ret);
state.FunctionState.PushVar(dummyForType);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
state.CGenPopStackSize();
return Operand.Create(op.Variable, 0);
case TypeCastType.PTR_TO_PTR:
// same as PTR_TO_INT32 then INT32_TO_PTR
ptrType = Type as PointerType;
if (ptrType == null) throw new InvalidProgramException().Attach(Expr);
op = state.FunctionState.PushType(state.TypeArrayOfPointer);
state.CGenPushStackSize();
dummyForType = state.FunctionState.PushType(state.EmitType(ptrType.RefType));
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
state.CGenPushStackSize();
state.FunctionState.PushVar(ret);
state.FunctionState.PushVar(dummyU32);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
state.CGenPopStackSize();
state.FunctionState.PushVar(op);
state.FunctionState.Push(dummyU32);
state.FunctionState.PushVar(dummyForType);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
state.CGenPopStackSize();
return Operand.Create(op.Variable, 0);
case TypeCastType.VARIANT_TO_COM_INTERFACE:
// this might be interface and might be dispatch.
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
state.CGenPushStackSize();
// u32 type = VarType(variant)
var varType = state.FunctionState.PushType(state.TypeU16);
state.CGenPushStackSize();
state.FunctionState.Push(ret);
state.FunctionState.PushVar(varType);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.VarType));
state.CGenPopStackSize();
// if (varType != vtDispatch) { regular com interface cast }
// else { cast to dispatch first }
// cast is same for both, just need to provide the "correct" type
const ushort VT_DISPATCH = 9;
state.CurrInsns.Add(Instruction.Create(OpCodes.Sub, varType, Operand.Create(VT_DISPATCH)));
// regular com interface cast
state.CGenPushStackSize();
// typeNo
state.FunctionState.PushType(state.TypeU32);
// self
var comInterface = state.FunctionState.AddLocal();
var insnDispatch = Instruction.Create(OpCodes.PushType, state.TypeIDispatch);
var insnEnd = Instruction.Create(OpCodes.Assign, comInterface, ret);
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpZ, insnDispatch, varType));
state.CurrInsns.Add(Instruction.Create(OpCodes.PushType, state.TypeIUnknown));
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, insnEnd));
state.CurrInsns.Add(insnDispatch);
state.CurrInsns.Add(insnEnd);
// retval
state.FunctionState.PushVar(op);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.ComInterfaceCast));
state.CGenPopStackSize();
return op;
case TypeCastType.COM_INTERFACE:
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
state.CGenPushStackSize();
// typeNo
state.FunctionState.PushType(state.TypeU32);
// self
state.FunctionState.Push(ret);
// retval
state.FunctionState.PushVar(op);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.ComInterfaceCast));
state.CGenPopStackSize();
return op;
case TypeCastType.NOP:
return ret;
case TypeCastType.COPY_CONSTRUCTOR:
if (retLoc == null) throw new InvalidOperationException("Cannot copy construct to an unknown variable").Attach(Expr);
state.CurrInsns.Add(Instruction.Create(OpCodes.Cpval, retLoc, ret));
return retLoc;
default:
op = retLoc != null ? retLoc : state.FunctionState.PushType(state.EmitType(Type));
state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, op, ret));
return op;
}
}
public override Operand CGenAddress(CGenState state, Operand retLoc)
{
throw new InvalidOperationException("Cannot get the address of a cast expression.").Attach(Expr);
}
public override bool CallerNeedsToCleanStack(CGenState state, bool retLocKnown, bool forAddress = false)
{
if (Kind == TypeCastType.NOP) return Expr.CallerNeedsToCleanStack(state, retLocKnown);
if (Expr.CallerNeedsToCleanStack(state, false)) return true;
return !retLocKnown;
}
}
}