using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Anonymus.x86
{
public class x86ConditionalJump : JumpInstruction
{
public string Condition;
public x86ConditionalJump(int Label, string Condition)
: base(Label)
{
this.Condition = Condition;
}
public override string GetCode(Compiler Comp)
{
return "\tj" + Condition + " _" + Label + "\n";
}
}
public class x86IdContainerData
{
public IdContainer Container;
public x86DataAllocator Allocator;
public x86IdContainerData(IdContainer Container)
{
this.Container = Container;
if (Container.FuncScope != null)
this.Allocator = new x86DataAllocator(Container);
}
public void Reset()
{
Allocator.Reset();
}
}
public class x86FuncContainerData : x86IdContainerData
{
public x86RegList UsedByParams;
public int PushedBytes;
public int FuncCallPushed;
public int StackAlloc;
public int ParamsLength;
public x86FuncContainerData(IdContainer Container)
: base(Container)
{
}
}
public class x86FuncScopeNode : FuncScopeNode
{
public x86Architecture Arch;
public bool AllocTmpRetVal = false;
public x86FuncScopeNode(x86Architecture Arch, IdContainer Parent, PString Name, FunctionType Type, bool IsStatic, PString[] Source)
: base(Parent, Name, Type, IsStatic, Source)
{
this.Arch = Arch;
var RetType = Type.RetType.RealType;
if (RetType is ValueType)
{
RetVar.Type = RetVar.Type.CreateRefType();
RetVar.PreAssigned = true;
Params.Insert(0, RetVar);
}
else
{
if (TmpRetVal != null)
AllocTmpRetVal = true;
if (RetType is NumberType || RetType is PointerType || RetType is BoolType ||
RetType is RefType || RetType is ClassType)
{
var Size = Type.RetType.Size;
var RegSize = Arch.RegSize;
if (Size > RegSize)
{
if (Size == 2 * RegSize) RetVar.ArchData = Arch.MultiRegPos(0, 2);
else throw new NotImplementedException();
}
else
{
if (Type.Conv == CallConv.AsCall)
RetVar.ArchData = new x86RegPosition(Arch, RetType.Size, 0);
else RetVar.ArchData = new x86RegPosition(Arch, RegSize, 0);
}
}
else if (!(RetType is FloatType) && !(RetType is VoidType))
{
throw new Exception("ERROR");
}
}
}
public bool ChkMem2Mem(CodeScopeNode Scope)
{
var CommCount = Scope.Commands.Count;
for (var CommId = 0; CommId < CommCount; CommId++)
{
var Comm = Scope.Commands[CommId];
foreach (var Node in Comm.EnumExpressions)
{
var Calcer = new x86DataPosCalcer(Comm, State, Node);
if (!Calcer.ChkMem2Mem())
return false;
}
foreach (var ChScope in Comm.EnumScopes)
if (!ChkMem2Mem(ChScope)) return false;
}
return true;
}
void Reset(IdContainer Container)
{
var ContainerData = Container.ArchData as x86IdContainerData;
ContainerData.Reset();
foreach (var Loc in Container.EnumLocals)
Loc.ArchData = null;
foreach (var Ch in Container.EnumChildren)
Reset(Ch);
}
x86DataAllocator CalcLocals(IdContainer Container)
{
var Data = Container.ArchData as x86IdContainerData;
var Ret = Data.Allocator.Copy();
foreach (var Ch in Container.EnumChildren)
Ret.SetUsed(CalcLocals(Ch));
foreach (var Loc in Container.EnumLocals)
{
var T = Loc.Type;
var DataCalcPos = !Loc.CanBeInReg || T is ValueType ?
x86DataCalcPos.Memory : x86DataCalcPos.GRegMem;
Loc.ArchData = Ret.Alloc(T.Size, Arch.GetAlign(T), DataCalcPos);
}
return Ret;
}
int PushPopRegs(Compiler Compiler, x86DataAllocator ContainerData, bool Push, CallConv Conv)
{
var UsedRegs = ContainerData.UsedRegs;
var Ret = 0;
if (Push)
{
for (var i = 0; i < UsedRegs.Size; i++)
if (PushPopReg(Compiler, Push, i, UsedRegs[i], Conv)) Ret++;
}
else
{
for (var i = UsedRegs.Size - 1; i >= 0; i--)
if (PushPopReg(Compiler, Push, i, UsedRegs[i], Conv)) Ret++;
}
return Ret;
}
private bool PushPopReg(Compiler Compiler, bool Push, int Index, int Size, CallConv Conv)
{
if (Size > 0 && !x86Architecture.IsVolatileReg(Index, Conv))
{
if (Push) Compiler.Append("\tpush ");
else Compiler.Append("\tpop ");
if (Size < 2) Size = 2;
Compiler.Append(x86Architecture.RegStr(Index, Size));
Compiler.Append("\n");
return true;
}
return false;
}
public override void CreateAsmCode()
{
CalcParamDataPos();
var Compiler = State.Arch.CreateCompiler(State);
var ContainerData = (x86DataAllocator)null;
var Dic = CalcPositions(ref ContainerData);
ReplaceJumpsFunc Func = C => EndFunction(C, ContainerData);
Compiler.SetJumpReplacing(RetLabel, Func);
BeginFunction(Compiler, ContainerData, Dic);
base.GetAsmCode(Compiler);
EndFunction(Compiler, ContainerData);
Compiler.Optimize();
AsmCode = Compiler.GetAssembly();
}
private void EndFunction(Compiler Compiler, x86DataAllocator ContainerData)
{
var Data = ArchData as x86FuncContainerData;
if (Data.StackAlloc > 0) Compiler.Append("\tadd esp, " + Data.StackAlloc + "\n");
PushPopRegs(Compiler, ContainerData, false, Type.Conv);
if (Type.Conv == CallConv.StdCall || Type.Conv == CallConv.AsCall)
{
if (Data.ParamsLength > 0)
Compiler.Append("\tret " + Data.ParamsLength.ToString() + "\n");
else Compiler.Append("\tret\n");
}
else if (Type.Conv == CallConv.CDecl)
{
Compiler.Append("\tret\n");
}
}
private void BeginFunction(Compiler Compiler, x86DataAllocator ContainerData, Dictionary<x86DataPosition, x86DataPosition> Dic)
{
var Data = ArchData as x86FuncContainerData;
Data.PushedBytes = PushPopRegs(Compiler, ContainerData, true, Type.Conv) * Arch.RegSize;
Data.StackAlloc = ContainerData.StackAlloc;
if (Data.StackAlloc > 0)
{
Data.StackAlloc = Helper.AdjustVarPos(Data.StackAlloc, Arch.RegSize);
Compiler.Append("\tsub esp, " + Data.StackAlloc.ToString() + "\n");
}
foreach (var e in Dic)
Compiler.Append("\tmov " + e.Value + ", " + e.Key + "\n");
}
private Dictionary<x86DataPosition, x86DataPosition> CalcPositions(ref x86DataAllocator ContainerData)
{
var Data = ArchData as x86FuncContainerData;
var Dic = (Dictionary<x86DataPosition, x86DataPosition>)null;
var VarPos = (Dictionary<LocalVariable, object>)null;
var Count = 0;
var ReCalc = false;
do
{
ReCalc = false;
Reset(this);
CalcExprRegs();
ContainerData = CalcLocals(this);
if (AllocTmpRetVal)
TmpRetVal.ArchData = ContainerData.Alloc(TmpRetVal.Type);
if (Dic == null)
{
Dic = new Dictionary<x86DataPosition, x86DataPosition>();
VarPos = new Dictionary<LocalVariable, object>();
foreach (var e in Params)
VarPos.Add(e, e.ArchData);
}
else
{
Dic.Clear();
foreach (var e in Params)
e.ArchData = VarPos[e];
}
foreach (var e in Params)
{
var Reg = e.ArchData as x86RegPosition;
if (Reg != null && ContainerData.UsedRegs[Reg.Index] > 0)
{
var P = ContainerData.Alloc(Reg.Size, Reg.Size);
e.ArchData = P;
Dic.Add(Reg, P);
if (Data.UsedByParams[Reg.Index] != 0) ReCalc = true;
Data.UsedByParams[Reg.Index] = 0;
}
}
Count++;
if (Count > 32) throw new Exception("ERROR");
} while (ReCalc || !ChkMem2Mem(this));
return Dic;
}
void OnParamCalced(Variable Var)
{
if (Var == RetVar && TmpRetVal != null)
{
TmpRetVal.PreAssigned = true;
TmpRetVal.Type = Var.Type;
TmpRetVal.ArchData = Var.ArchData;
}
}
protected void CalcParamDataPos()
{
var Data = ArchData as x86FuncContainerData;
var NParams = Params;
if (Type.Conv == CallConv.AsCall)
{
Data.UsedByParams = new x86RegList(Arch.RegCount);
NParams = Params.ToList();
var Index = 0;
var Types = new Type[Params.Count];
for (var i = 0; i < Params.Count; i++)
Types[i] = Params[i].Type;
foreach (var i in Arch.EnumFirstAsCallParams(Types))
{
var V = Params[i];
var Reg = x86DataAllocator.RegAllocSequence[Index];
Data.UsedByParams.SetUsed(Reg, V.Type.Size);
V.ArchData = new x86RegPosition(Arch, V.Type.Size, Reg);
OnParamCalced(V);
NParams.Remove(V);
Index++;
}
}
var P = 0;
foreach (var V in NParams)
{
P = Helper.AdjustVarPos(P, Arch.RegSize);
var S = V.Type.Size;
V.ArchData = new x86StackPosition(this, S, P, true);
OnParamCalced(V);
P += S;
}
P = Helper.AdjustVarPos(P, Arch.RegSize);
Data.ParamsLength = P;
}
}
public class x86MoveCondBranch : CondBranch
{
public x86DataPosition Dst;
public x86DataPosition Src;
public x86ExprScope Scope;
public bool SrcSigned;
public x86MoveCondBranch(x86DataPosition Dst, x86DataPosition Src,
x86ExprScope Scope, bool SrcSigned = false)
{
this.Dst = Dst;
this.Src = Src;
this.Scope = Scope;
this.SrcSigned = SrcSigned;
}
}
public class x86Architecture : Architecture
{
public bool x86_64;
public override int RegSize { get { return x86_64 ? 8 : 4; } }
public override int RegCount { get { return x86_64 ? 15 : 7; } }
public override int MaxStructPow2Size { get { return 16; } }
public int ByteRegCount { get { return x86_64 ? 14 : 4; } }
public static bool NeedLoadFloatDst(ExpressionNode Dst, bool Mod)
{
if (Dst is IdExpressionNode && Mod) return true;
if (Dst is ConstExpressionNode) return true;
var OpNode = Dst as OpExpressionNode;
if (OpNode == null) return false;
var Op = OpNode.Operator;
if ((Op == Operator.Member || Op == Operator.Index) && Mod) return true;
return false;
}
public static x86DataCalcPos TypePos(Type T)
{
T = T.RealType;
if (T is FloatType) return x86DataCalcPos.Memory;
else if (T is ValueType) return x86DataCalcPos.Memory;
else return x86DataCalcPos.GRegMem;
}
public x86RegPosition GetArgP(ref int Index, int Size)
{
var Reg = x86DataAllocator.RegAllocSequence[Index];
Index++;
return new x86RegPosition(this, Size, Reg);
}
public IEnumerable<int> EnumFirstAsCallParams(Type[] Params, int Count = 0)
{
for (var i = 0; i < Params.Length; i++)
{
var T = Params[i];
if (T is RefType || T is ClassType || T is PointerType)
{
yield return i;
Count++;
if (Count > 2) yield break;
}
}
if (Count > 2) yield break;
for (var i = 0; i < Params.Length; i++)
{
var T = Params[i];
if (T is NonFltNumType && T.Size <= RegSize)
{
yield return i;
Count++;
if (Count > 2) yield break;
}
}
}
public override int GetAlign(Type Type)
{
Type = Type.RealType;
if (Type is NumberType || Type is BoolType)
{
var S = Type.Size;
var RS = RegSize;
if (S <= RS) return S;
else return RS;
}
else if (Type is PointerType || Type is StructuredType ||
Type is FunctionType || Type is RefType)
{
return RegSize;
}
else
{
throw new NotImplementedException();
}
}
public override void OnNewIdentifier(Identifier Id)
{
var Asm = Id.AsmName;
if (Id is Variable)
{
var Var = Id as Variable;
var Type = Var.Type;
if (Id is GlobalVariable)
{
if (Id.Container is ImportScopeNode) throw new Exception("ERROR");
Id.ArchData = new x86MemPosition(this, Type.Size, Asm, 0, true, 16);
return;
}
}
else if (Id is Function)
{
Id.ArchData = new x86MemPosition(this, RegSize,
Asm, 0, Id.Container is ImportScopeNode);
return;
}
}
public x86Architecture(bool x86_64)
{
this.x86_64 = x86_64;
}
public override void CalcExprDataPos(CompilerState State, IdContainer Container, ExpressionNode Expr)
{
var RegCalcer = new x86DataPosCalcer(Container, State, Expr);
RegCalcer.Calc();
}
public override void OnNewContainer(IdContainer Container)
{
if (Container is FuncScopeNode)
{
Container.ArchData = new x86FuncContainerData(Container);
}
else if (Container is GlobalScopeNode)
{
var Glb = Container as GlobalScopeNode;
if (Glb.ExternScopes == null) Glb.ExternScopes = new List<ExternScopeNode>();
var Scope = new ExternScopeNode(Glb, null);
Glb.ExternScopes.Add(Scope);
var L = new List<Identifier>();
L.Add(CreateFunc(Scope, CallConv.AsCall, "ULongToFloat", "float", "ulong"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "FloatToULong", "ulong", "float"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "DoubleToULong", "ulong", "double"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "LongMul", "long", "long", "long"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "LongDiv", "long", "long", "long"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "ULongDiv", "ulong", "ulong", "ulong"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "LongMod", "long", "long", "long"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "ULongMod", "ulong", "ulong", "ulong"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "LongShiftLeft", "long", "long", "long"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "LongShiftRight", "long", "long", "long"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "ULongShiftRight", "ulong", "ulong", "ulong"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "Math_ArcSine", "double", "double"));
L.Add(CreateFunc(Scope, CallConv.AsCall, "Math_ArcCosine", "double", "double"));
Glb.Identifiers.AddRange(L);
}
else
{
Container.ArchData = new x86IdContainerData(Container);
}
}
private Function CreateFunc(ScopeNode Scope, CallConv Conv,
string FuncName, string RetType, params string[] Params)
{
var RType = Scope.GetId<Type>(new PString(RetType));
if (RType == null) throw new Exception("ERROR");
var DeclList = new VarDeclList();
foreach (var e in Params)
{
var T = Scope.GetId<Type>(new PString(e));
DeclList.Add(new VarDecl(null, null, T, null, null));
}
var Type = new FunctionType(Scope, new PString(FuncName), Conv, RType, DeclList);
var Func = new Function(Scope, new PString(FuncName), Type, null);
Func.DeclInThis = false;
OnNewIdentifier(Func);
if (!Scope.CanIdDeclared(Func)) return null;
Scope.Identifiers.Add(Func);
return Func;
}
public override Compiler CreateCompiler(CompilerState State)
{
return new x86Compiler(this, State);
}
public override ExprPlugIn CreatePlugIn(IdContainer Container)
{
return new x86PlugIn(this, Container);
}
public override FuncScopeNode CreateFuncScope(IdContainer Parent,
PString Name, FunctionType Type, bool IsStatic, PString[] Source)
{
var Ret = new x86FuncScopeNode(this, Parent, Name, Type, IsStatic, Source);
OnNewContainer(Ret);
return Ret;
}
public bool IsCanbeReg(int RegId, int Size)
{
return RegId < RegCount && Size <= RegSize && (Size != 1 || RegId < ByteRegCount);
}
public static bool IsRegPos(x86DataPosition Pos, int Index)
{
var RegPos = Pos as x86RegPosition;
return RegPos != null && RegPos.Index == Index;
}
public bool RegExists(int Index, int Size, bool HighBytes)
{
if (Index >= RegCount) return false;
if (Size > 1) return true;
if (Index < 4) return true;
if ((Index == 4 || Index == 5) && x86_64) return true;
return false;
}
public static string GetTypeString(int Size)
{
switch (Size)
{
case 1: return "byte";
case 2: return "word";
case 4: return "dword";
case 6: return "pword";
case 8: return "qword";
case 10: return "tword";
case 16: return "dqword";
default: return null;
}
}
public static string GetDataTypeString(int Size)
{
switch (Size)
{
case 1: return "db";
case 2: return "dw";
case 4: return "dd";
case 6: return "dp";
case 8: return "dq";
case 10: return "dt";
default: return null;
}
}
public static bool IsVolatileReg(int Id, CallConv Conv)
{
if (Conv == CallConv.CDecl || Conv == CallConv.StdCall)
{
if (Id == 0) return true;
if (Id == 1) return true;
if (Id == 2) return true;
if (Id == 7) return true;
if (Id == 8) return true;
if (Id == 9) return true;
if (Id == 10) return true;
}
else if (Conv == CallConv.AsCall)
{
if (Id == 0) return true;
if (Id == 1) return true;
if (Id == 2) return true;
if (Id == 3) return true;
if (Id == 7) return true;
if (Id == 8) return true;
if (Id == 9) return true;
if (Id == 10) return true;
}
else
{
throw new Exception("ERROR");
}
return false;
}
public static string RegStr(int Id, int Size, bool HighBytes = false)
{
if (Size == 1)
{
switch (Id)
{
case 0: return HighBytes ? "ah" : "al";
case 1: return HighBytes ? "ch" : "cl";
case 2: return HighBytes ? "dh" : "dl";
case 3: return HighBytes ? "bh" : "bl";
case 4: return "sil";
case 5: return "dil";
default:
if (Id > 6 && Id < 16) return "r" + Id + "b";
else return null;
}
}
else if (Size == 2)
{
switch (Id)
{
case 0: return "ax";
case 1: return "cx";
case 2: return "dx";
case 3: return "bx";
case 4: return "si";
case 5: return "di";
case 6: return "bp";
default:
if (Id > 6 && Id < 16) return "r" + Id + "w";
else return null;
}
}
else if (Size == 4)
{
switch (Id)
{
case 0: return "eax";
case 1: return "ecx";
case 2: return "edx";
case 3: return "ebx";
case 4: return "esi";
case 5: return "edi";
case 6: return "ebp";
default:
if (Id > 6 && Id < 16) return "r" + Id + "d";
else return null;
}
}
else if (Size == 8)
{
switch (Id)
{
case 0: return "rax";
case 1: return "rcx";
case 2: return "rdx";
case 3: return "rbx";
case 4: return "rsi";
case 5: return "rdi";
case 6: return "rbp";
default:
if (Id > 6 && Id < 16) return "r" + Id;
else return null;
}
}
else
{
return null;
}
}
public static string OpInstruction(Operator Op, bool Signed)
{
if (Op == Operator.Equal) return "e";
if (Op == Operator.Nonequal) return "ne";
if (Op == Operator.Less) return Signed ? "l" : "b";
if (Op == Operator.LessEqual) return Signed ? "le" : "be";
if (Op == Operator.Greater) return Signed ? "g" : "a";
if (Op == Operator.GreaterEqual) return Signed ? "ge" : "ae";
return null;
}
public x86MultiPosition MultiRegPos(params int[] Positions)
{
var RetPositions = new x86DataPosition[Positions.Length];
for (var i = 0; i < Positions.Length; i++)
RetPositions[i] = new x86RegPosition(this, RegSize, Positions[i]);
return new x86MultiPosition(this, Positions.Length * RegSize, RetPositions);
}
public x86MoveCondBranch GetMoveCondBranch(GlobalScopeNode Global, Type SrcType,
x86DataPosition From, x86DataPosition To, x86ExprScope Scope, bool ConvConst)
{
if (To != null && To.Size > 1 && !(To is x86MultiPosition) && !(To is x86MemPosition))
{
if (ConvConst && From is x86ConstPosition)
{
var ConstPos = From as x86ConstPosition;
var GlbVar = Global.CreateExprConst(ConstPos.Data, SrcType);
From = GlbVar.ArchData as x86DataPosition;
}
if (Scope == null) { ; }
return new x86MoveCondBranch(To, From, Scope, SrcType is SignedType);
}
return null;
}
public x86MoveCondBranch GetCastConstBranch(Compiler Compiler, GlobalScopeNode Global,
ExpressionNode Node, bool ConvConst)
{
var CastNode = Node as CastExpressionNode;
if (CastNode == null) return null;
var Ch = CastNode.Child;
if (!Ch.Type.IsEqual(Node.Type)) return null;
var x86Comp = Compiler as x86Compiler;
var From = x86Comp.GetNodePos(Ch, GetNodePosMode.RetNull);
var To = x86Comp.GetNodePos(Node, GetNodePosMode.StrOnly);
if (From != null && To != null)
{
var Data = Node.ArchData as x86NodeData;
return GetMoveCondBranch(Global, Ch.Type, From, To, Data.Scope, ConvConst);
}
return null;
}
public x86MoveCondBranch GetAssignmentBranch(Compiler Compiler, GlobalScopeNode Global,
ExpressionNode Node, bool ConvConst)
{
var OpNode = Node as OpExpressionNode;
if (OpNode == null) return null;
var Op = OpNode.Operator;
var Ch = OpNode.Children;
var x86Comp = Compiler as x86Compiler;
var To = x86Comp.GetNodePos(Ch[0], GetNodePosMode.RetNull);
var From = x86Comp.GetNodePos(Ch[1], GetNodePosMode.RetNull);
if (From == null && To != null && Ch[1].GetPosition().IsEqual(To))
return GetCastConstBranch(Compiler, Global, Ch[1], ConvConst);
if (From != null && To != null)
{
var Data = Node.ArchData as x86NodeData;
return GetMoveCondBranch(Global, Ch[0].Type, From, To, Data.Scope, ConvConst);
}
return null;
}
public CondBranch GetNodeCondBrach(Compiler Compiler, GlobalScopeNode Global,
ExpressionNode Node, bool ConvConst, bool RetNull = false, bool EnableMoveBranch = true)
{
if (EnableMoveBranch)
{
if (Node is CastExpressionNode)
{
var Ret = GetCastConstBranch(Compiler, Global, Node, ConvConst);
if (Ret != null) return Ret;
}
else if (Node is OpExpressionNode)
{
var Ret = GetAssignmentBranch(Compiler, Global, Node, ConvConst);
if (Ret != null) return Ret;
}
}
if (RetNull) return null;
return new CodeCondBranch((Comp) => { Comp.GetExprCode(Node); });
}
private ExpressionNode GetExpr(Command Obj)
{
var ExprComm = Obj as ExpressionCommand;
if (ExprComm == null) return null;
return ExprComm.Expression;
}
public override CondBranch[] GetBraches(Compiler Compiler, GlobalScopeNode Global,
Command Then, Command Else, ref ExpressionNode Condition)
{
if (Then is JumpCommand || Else is JumpCommand)
{
var GotoThen = Then as JumpCommand;
var GotoElse = Else as JumpCommand;
var RetThen = GotoThen != null ? new JumpCodeBranch(GotoThen.Label) : null;
var RetElse = GotoElse != null ? new JumpCodeBranch(GotoElse.Label) : null;
return new CondBranch[] { RetThen, RetElse };
}
var OpCond = Condition as OpExpressionNode;
if (!Operators.IsRelEqualityOp(OpCond.Operator))
return new CondBranch[] { null, null };
var ExprElse = GetNodeCondBrach(Compiler, Global, GetExpr(Else), false, true);
var MoveElse = ExprElse as x86MoveCondBranch;
var ConvConst = MoveElse != null && MoveElse.Src is x86ConstPosition;
var ExprThen = GetNodeCondBrach(Compiler, Global, GetExpr(Then), ConvConst, true);
var MoveThen = ExprThen as x86MoveCondBranch;
if (!ConvConst && MoveThen != null && MoveThen.Src is x86ConstPosition)
{
OpCond.Operator = Operators.NegateOp(OpCond.Operator);
return new CondBranch[] { ExprElse, ExprThen };
}
return new CondBranch[] { ExprThen, ExprElse };
}
}
}