IFPSTools.NET/LibIFPSCC/CGen/Statements.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

333 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using CodeGeneration;
using IFPSLib.Emit;
namespace ABT {
public abstract partial class Stmt {
public abstract void CGenStmt(Env env, CGenState state);
public void Pop(CGenState state)
{
state.CGenPopStackSize();
}
public Operand CGenExprStmt(Env env, Expr expr, CGenState state, bool cleanUp = false) {
state.CGenPushStackSize();
var ret = expr.CGenValue(state, null);
if (cleanUp) Pop(state);
return ret;
}
}
public sealed partial class GotoStmt {
public override void CGenStmt(Env env, CGenState state) {
state.JumpTo(state.GotoLabel(this.Label), false);
}
}
public sealed partial class LabeledStmt {
public override void CGenStmt(Env env, CGenState state) {
var label = state.GotoLabel(this.Label);
state.CGenLabel(label);
this.Stmt.CGenStmt(env, state);
}
}
public sealed partial class ContStmt {
public override void CGenStmt(Env env, CGenState state) {
state.JumpTo(state.ContinueLabel, false);
}
}
public sealed partial class BreakStmt {
public override void CGenStmt(Env env, CGenState state) {
state.JumpTo(state.BreakLabel, state.IsInSwitch);
}
}
public sealed partial class ExprStmt {
public override void CGenStmt(Env env, CGenState state) {
if (this.ExprOpt.IsSome) {
Int32 stack_size = state.StackSize;
this.ExprOpt.Value.CGenValue(state, null);
state.CGenForceStackSizeTo(stack_size);
}
}
}
public sealed partial class CompoundStmt {
public override void CGenStmt(Env env, CGenState state) {
state.CGenPushStackSize();
foreach (Tuple<Env, Decln> decln in this.Declns) {
decln.Item2.CGenDecln(decln.Item1, state);
}
foreach (Tuple<Env, Stmt> stmt in this.Stmts) {
stmt.Item2.CGenStmt(stmt.Item1, state);
}
state.CGenPopStackSize();
}
}
public sealed partial class ReturnStmt {
public override void CGenStmt(Env env, CGenState state) {
// remove all pop instructions leading up to here.
// about to return, runtime cleans up the stack itself on ret
while (state.CurrInsns.Count > 0 && state.CurrInsns.Last().OpCode.Code == Code.Pop) state.CurrInsns.RemoveAt(state.CurrInsns.Count - 1);
if (this.ExprOpt.IsSome) {
var retval = state.FunctionState.Function.CreateReturnVariable();
// Is this function supposed to return a pointer?
var ptr = ExprOpt.Value.Type as PointerType;
var needsPtrCast = ptr != null && ptr.IsRef;
// Evaluate the Value.
var retOp = Operand.Create(retval);
var val = ExprOpt.Value.CGenValue(state, needsPtrCast ? null : retOp);
// Remove all ending pops to save space. ret instruction cleans up stack itself
while (state.CurrInsns.LastOrDefault()?.OpCode.Code == Code.Pop) state.CurrInsns.RemoveAt(state.CurrInsns.Count - 1);
// Is this function supposed to return a pointer?
if (needsPtrCast)
{
// Ptr-to-ptr cast into retval.
// Don't bother cleaning up the stack at all, we are about to return.
var dummyForType = state.FunctionState.PushType(state.EmitType(ptr.RefType));
var dummyU32 = state.FunctionState.PushType(state.TypeU32);
state.FunctionState.PushVar(val);
state.FunctionState.PushVar(dummyU32);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastPointerRef));
state.FunctionState.PushVar(Operand.Create(retval));
state.FunctionState.Push(dummyU32);
state.FunctionState.PushVar(dummyForType);
state.CurrInsns.Add(Instruction.Create(OpCodes.Call, state.CastRefPointer));
state.CurrInsns.Add(Instruction.Create(OpCodes.Ret));
return;
}
if (val != retOp) state.CurrInsns.Add(Instruction.Create(OpCodes.Assign, retOp, val));
}
// Return, runtime will fix up stack itself.
state.CurrInsns.Add(Instruction.Create(OpCodes.Ret));
}
}
public sealed partial class WhileStmt {
public override void CGenStmt(Env env, CGenState state) {
Int32 start_label = state.RequestLabel();
Int32 finish_label = state.RequestLabel();
// start:
state.CGenLabel(start_label);
// jz finish, Cond
var ret = CGenExprStmt(env, this.Cond, state);
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
Pop(state);
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[finish_label]));
// Body
state.InLoop(start_label, finish_label);
this.Body.CGenStmt(env, state);
state.OutLabels();
// jmp start
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[start_label]));
// finish:
state.CGenLabel(finish_label);
}
}
public sealed partial class DoWhileStmt {
public override void CGenStmt(Env env, CGenState state) {
Int32 start_label = state.RequestLabel();
Int32 finish_label = state.RequestLabel();
Int32 continue_label = state.RequestLabel();
// start:
state.CGenLabel(start_label);
// Body
state.InLoop(continue_label, finish_label);
this.Body.CGenStmt(env, state);
state.OutLabels();
state.CGenLabel(continue_label);
// test Cond
var ret = CGenExprStmt(env, this.Cond, state);
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagNZ, ret));
Pop(state);
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[start_label]));
state.CGenLabel(finish_label);
}
}
public sealed partial class ForStmt {
public override void CGenStmt(Env env, CGenState state) {
// Init
this.Init.Map(_ => CGenExprStmt(env, _, state, true));
Int32 start_label = state.RequestLabel();
Int32 finish_label = state.RequestLabel();
Int32 continue_label = state.RequestLabel();
// start:
state.CGenLabel(start_label);
// test cond
this.Cond.Map(_ => {
var ret = CGenExprStmt(env, _, state);
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
Pop(state);
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[finish_label]));
return ret;
});
// Body
state.InLoop(continue_label, finish_label);
this.Body.CGenStmt(env, state);
state.OutLabels();
// continue:
state.CGenLabel(continue_label);
// Loop
this.Loop.Map(_ => CGenExprStmt(env, _, state, true));
// jmp start
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[start_label]));
// finish:
state.CGenLabel(finish_label);
}
}
public sealed partial class SwitchStmt {
public override void CGenStmt(Env env, CGenState state) {
// Inside a switch statement, the initializations are ignored,
// but the stack size should be changed.
List<Tuple<Env, Decln>> declns;
List<Tuple<Env, Stmt>> stmts;
var compoundStmt = this.Stmt as CompoundStmt;
if (compoundStmt == null) {
throw new NotImplementedException().Attach(Expr);
}
declns = compoundStmt.Declns;
stmts = compoundStmt.Stmts;
// Track all case values.
IReadOnlyList<Int32> values = CaseLabelsGrabber.GrabLabels(this);
// Make sure there are no duplicates.
if (values.Distinct().Count() != values.Count) {
throw new InvalidOperationException("case labels not unique.").Attach(Expr);
}
// Request labels for these values.
Dictionary<Int32, Int32> value_to_label = values.ToDictionary(value => value, value => state.RequestLabel());
Int32 label_finish = state.RequestLabel();
Int32 num_default_stmts = stmts.Count(_ => _.Item2 is DefaultStmt);
if (num_default_stmts > 1) {
throw new InvalidOperationException("duplicate defaults.").Attach(Expr);
}
Int32 label_default =
num_default_stmts == 1 ?
state.RequestLabel() :
label_finish;
//Int32 saved_stack_size = state.StackSize;
// 1. Evaluate Expr.
var op = CGenExprStmt(env, this.Expr, state);
// 2. Expand stack.
//state.CGenForceStackSizeTo(stack_size);
// 3. Make the Jump list.
var cmp = state.FunctionState.PushType(state.TypeU32);
foreach (KeyValuePair<Int32, Int32> value_label_pair in value_to_label)
{
state.CurrInsns.Add(Instruction.Create(OpCodes.Eq, cmp, op, Operand.Create(value_label_pair.Key)));
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpNZ, state.FunctionState.Labels[value_label_pair.Value], cmp));
}
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[label_default]));
// 4. List all the statements.
state.InSwitch(label_finish, label_default, value_to_label);
foreach (Tuple<Env, Stmt> env_stmt_pair in stmts) {
env_stmt_pair.Item2.CGenStmt(env_stmt_pair.Item1, state);
}
state.OutLabels();
// 5. finish:
state.CGenLabel(label_finish);
// 6. Restore stack size.
Pop(state);
}
}
public sealed partial class CaseStmt {
public override void CGenStmt(Env env, CGenState state) {
Int32 label = state.CaseLabel(this.Value);
state.CGenLabel(label);
this.Stmt.CGenStmt(env, state);
}
}
public sealed partial class DefaultStmt {
public override void CGenStmt(Env env, CGenState state) {
Int32 label = state.DefaultLabel;
state.CGenLabel(label);
this.Stmt.CGenStmt(env, state);
}
}
public sealed partial class IfStmt {
public override void CGenStmt(Env env, CGenState state) {
var ret = CGenExprStmt(env, this.Cond, state);
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
Pop(state);
Int32 finish_label = state.RequestLabel();
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[finish_label]));
this.Stmt.CGenStmt(env, state);
state.CGenLabel(finish_label);
}
}
public sealed partial class IfElseStmt {
public override void CGenStmt(Env env, CGenState state) {
var ret = CGenExprStmt(env, this.Cond, state);
state.CurrInsns.Add(Instruction.Create(OpCodes.SetFlagZ, ret));
Pop(state);
Int32 false_label = state.RequestLabel();
Int32 finish_label = state.RequestLabel();
state.CurrInsns.Add(Instruction.Create(OpCodes.JumpF, state.FunctionState.Labels[false_label]));
this.TrueStmt.CGenStmt(env, state);
state.CurrInsns.Add(Instruction.Create(OpCodes.Jump, state.FunctionState.Labels[finish_label]));
state.CGenLabel(false_label);
this.FalseStmt.CGenStmt(env, state);
state.CGenLabel(finish_label);
}
}
}