diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 5ef7b9bcc..d75000d41 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -42,12 +42,14 @@ #include "m_random.h" + #define CHECKRESOLVED() if (isresolved) return this; isresolved=true; #define SAFE_DELETE(p) if (p!=NULL) { delete p; p=NULL; } #define RESOLVE(p,c) if (p!=NULL) p = p->Resolve(c) #define ABORT(p) if (!(p)) { delete this; return NULL; } #define SAFE_RESOLVE(p,c) RESOLVE(p,c); ABORT(p) +class VMFunctionBuilder; extern PSymbolTable GlobalSymbols; //========================================================================== @@ -142,6 +144,15 @@ struct ExpVal }; +struct ExpEmit +{ + ExpEmit() : RegNum(0), RegType(REGT_NIL) {} + ExpEmit(int reg, int type) : RegNum(reg), RegType(type) {} + ExpEmit(int reg, int type, bool konst) : RegNum(reg), RegType(type), Konst(konst) {} + ExpEmit(VMFunctionBuilder *build, int type); + + BYTE RegNum, RegType, Konst; +}; //========================================================================== // @@ -166,12 +177,25 @@ public: virtual bool isConstant() const; virtual void RequestAddress(); + virtual ExpEmit Emit(VMFunctionBuilder *build); + FScriptPosition ScriptPosition; FExpressionType ValueType; bool isresolved; }; +class FxParameter : public FxExpression +{ + FxExpression *Operand; + +public: + FxParameter(FxExpression*); + ~FxParameter(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxIdentifier @@ -310,6 +334,21 @@ public: FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +class FxFloatCast : public FxExpression +{ + FxExpression *basex; + +public: + + FxFloatCast(FxExpression *x); + ~FxFloatCast(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; @@ -327,6 +366,7 @@ public: FxPlusSign(FxExpression*); ~FxPlusSign(); FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -344,6 +384,7 @@ public: ~FxMinusSign(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -361,6 +402,7 @@ public: ~FxUnaryNotBitwise(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -378,6 +420,7 @@ public: ~FxUnaryNotBoolean(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -396,6 +439,7 @@ public: FxBinary(int, FxExpression*, FxExpression*); ~FxBinary(); bool ResolveLR(FCompileContext& ctx, bool castnumeric); + void Promote(); }; //========================================================================== @@ -411,6 +455,7 @@ public: FxAddSub(int, FxExpression*, FxExpression*); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -426,6 +471,7 @@ public: FxMulDiv(int, FxExpression*, FxExpression*); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -441,6 +487,7 @@ public: FxCompareRel(int, FxExpression*, FxExpression*); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -456,6 +503,7 @@ public: FxCompareEq(int, FxExpression*, FxExpression*); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -471,6 +519,7 @@ public: FxBinaryInt(int, FxExpression*, FxExpression*); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -491,6 +540,7 @@ public: FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -511,6 +561,7 @@ public: FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -530,6 +581,7 @@ public: FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -551,6 +603,7 @@ public: FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -564,6 +617,7 @@ class FxFRandom : public FxRandom public: FxFRandom(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -584,6 +638,7 @@ public: FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; @@ -623,6 +678,7 @@ public: FxExpression *Resolve(FCompileContext&); void RequestAddress(); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -637,6 +693,7 @@ public: FxSelf(const FScriptPosition&); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -657,6 +714,7 @@ public: FxExpression *Resolve(FCompileContext&); //void RequestAddress(); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; @@ -700,6 +758,7 @@ public: ~FxActionSpecialCall(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -719,6 +778,7 @@ public: ~FxGlobalFunctionCall(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; @@ -739,6 +799,7 @@ public: ~FxClassTypeCast(); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== @@ -775,6 +836,7 @@ public: FxMultiNameState(const char *statestring, const FScriptPosition &pos); FxExpression *Resolve(FCompileContext&); ExpVal EvalExpression (AActor *self); + ExpEmit Emit(VMFunctionBuilder *build); }; diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 60a01586b..eb02a1892 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -49,6 +49,7 @@ #include "p_lnspec.h" #include "doomstat.h" #include "thingdef_exp.h" +#include "vmbuilder.h" // Accessible actor member variables DEFINE_MEMBER_VARIABLE(alpha, AActor) @@ -77,6 +78,11 @@ DEFINE_MEMBER_VARIABLE(Damage, AActor) DEFINE_MEMBER_VARIABLE(Score, AActor) DEFINE_MEMBER_VARIABLE(uservar, AActor) +ExpEmit::ExpEmit(VMFunctionBuilder *build, int type) +: RegNum(build->Registers[type].Get(1)), RegType(type) +{ +} + //========================================================================== // // EvalExpression @@ -244,6 +250,18 @@ ExpVal FxExpression::EvalExpression (AActor *self) return val; } +//========================================================================== +// +// +// +//========================================================================== + +ExpEmit FxExpression::Emit (VMFunctionBuilder *build) +{ + ScriptPosition.Message(MSG_ERROR, "Unemitted expression found"); + return ExpEmit(); +} + //========================================================================== // @@ -306,6 +324,80 @@ void FxExpression::RequestAddress() ScriptPosition.Message(MSG_ERROR, "invalid dereference\n"); } +//========================================================================== +// +// +// +//========================================================================== + +FxParameter::FxParameter(FxExpression *operand) +: FxExpression(operand->ScriptPosition) +{ + Operand = operand; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxParameter::~FxParameter() +{ + SAFE_DELETE(Operand); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxParameter::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Operand, ctx); + return this; +} + +ExpEmit FxParameter::Emit(VMFunctionBuilder *build) +{ + if (Operand->isConstant()) + { + ExpVal val = Operand->EvalExpression(NULL); + if (val.Type == VAL_Int || val.Type == VAL_Sound || val.Type == VAL_Name || val.Type == VAL_Color) + { + build->Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, build->GetConstantInt(val.Int)); + } + else if (val.Type == VAL_Float) + { + build->Emit(OP_PARAM, 0, REGT_FLOAT | REGT_KONST, build->GetConstantFloat(val.Int)); + } + else if (val.Type == VAL_Class || val.Type == VAL_Object) + { + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(val.pointer, ATAG_OBJECT)); + } + else if (val.Type == VAL_State) + { + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(val.pointer, ATAG_STATE)); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Cannot emit needed constant"); + } + } + else + { + ExpEmit where = Operand->Emit(build); + + if (where.RegNum == REGT_NIL) + { + ScriptPosition.Message(MSG_ERROR, "Attempted to pass a non-value"); + build->Emit(OP_PARAM, 0, where.RegType, where.RegNum); + } + } + return ExpEmit(); +} //========================================================================== // @@ -353,8 +445,6 @@ FxExpression *FxConstant::MakeConstant(PSymbol *sym, const FScriptPosition &pos) return x; } - - //========================================================================== // // @@ -430,6 +520,100 @@ ExpVal FxIntCast::EvalExpression (AActor *self) return baseval; } +ExpEmit FxIntCast::Emit(VMFunctionBuilder *build) +{ + ExpEmit from = basex->Emit(build); + assert(!from.Konst); + assert(basex->ValueType == VAL_Float); + ExpEmit to(build, REGT_INT); + build->Emit(OP_CAST, to.RegNum, from.RegNum, CAST_F2I); + return to; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxFloatCast::FxFloatCast(FxExpression *x) +: FxExpression(x->ScriptPosition) +{ + basex=x; + ValueType = VAL_Float; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxFloatCast::~FxFloatCast() +{ + SAFE_DELETE(basex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxFloatCast::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(basex, ctx); + + if (basex->ValueType == VAL_Float) + { + FxExpression *x = basex; + basex = NULL; + delete this; + return x; + } + else if (basex->ValueType == VAL_Int) + { + if (basex->isConstant()) + { + ExpVal constval = basex->EvalExpression(NULL); + FxExpression *x = new FxConstant(constval.GetFloat(), ScriptPosition); + delete this; + return x; + } + return this; + } + else + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return NULL; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxFloatCast::EvalExpression (AActor *self) +{ + ExpVal baseval = basex->EvalExpression(self); + baseval.Float = baseval.GetFloat(); + baseval.Type = VAL_Float; + return baseval; +} + +ExpEmit FxFloatCast::Emit(VMFunctionBuilder *build) +{ + ExpEmit from = basex->Emit(build); + assert(!from.Konst); + assert(basex->ValueType == VAL_Int); + ExpEmit to(build, REGT_FLOAT); + build->Emit(OP_CAST, to.RegNum, from.RegNum, CAST_I2F); + return to; +} //========================================================================== // @@ -480,6 +664,11 @@ FxExpression *FxPlusSign::Resolve(FCompileContext& ctx) } } +ExpEmit FxPlusSign::Emit(VMFunctionBuilder *build) +{ + return Operand->Emit(build); +} + //========================================================================== // // @@ -559,6 +748,23 @@ ExpVal FxMinusSign::EvalExpression (AActor *self) return ret; } +ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build) +{ + assert(ValueType.Type == Operand->ValueType.Type); + ExpEmit from = Operand->Emit(build); + assert(from.Konst != 0); + // Do it in-place. + if (ValueType == VAL_Int) + { + build->Emit(OP_NEG, from.RegNum, from.RegNum, 0); + } + else + { + assert(ValueType == VAL_Float); + build->Emit(OP_NEG, from.RegNum, from.RegNum, 0); + } + return from; +} //========================================================================== // @@ -639,6 +845,17 @@ ExpVal FxUnaryNotBitwise::EvalExpression (AActor *self) return ret; } +ExpEmit FxUnaryNotBitwise::Emit(VMFunctionBuilder *build) +{ + assert(ValueType.Type == Operand->ValueType.Type); + assert(ValueType == VAL_Int); + ExpEmit from = Operand->Emit(build); + assert(from.Konst != 0); + // Do it in-place. + build->Emit(OP_NOT, from.RegNum, from.RegNum, 0); + return from; +} + //========================================================================== // // @@ -716,6 +933,36 @@ ExpVal FxUnaryNotBoolean::EvalExpression (AActor *self) return ret; } +ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build) +{ + ExpEmit from = Operand->Emit(build); + assert(from.Konst != 0); + ExpEmit to(build, REGT_INT); + build->FreeReg(from.RegType, from.RegNum); + + // Preload result with 0. + build->Emit(OP_LI, to.RegNum, 0, 0); + + // Check source against 0. + if (from.RegType == REGT_INT) + { + build->Emit(OP_EQ_R, 0, from.RegNum, to.RegNum); + } + else if (from.RegType == REGT_FLOAT) + { + build->Emit(OP_EQF_K, 0, from.RegNum, build->GetConstantFloat(0)); + } + else if (from.RegNum == REGT_POINTER) + { + build->Emit(OP_EQA_K, 0, from.RegNum, build->GetConstantAddress(NULL, ATAG_GENERIC)); + } + build->Emit(OP_JMP, 1); + + // Reload result with 1 if the comparison fell through. + build->Emit(OP_LI, to.RegNum, 1); + return to; +} + //========================================================================== // // @@ -786,6 +1033,17 @@ bool FxBinary::ResolveLR(FCompileContext& ctx, bool castnumeric) return true; } +void FxBinary::Promote() +{ + if (left->ValueType == VAL_Float && right->ValueType == VAL_Int) + { + right = new FxFloatCast(right); + } + else if (left->ValueType == VAL_Int && right->ValueType == VAL_Float) + { + left = new FxFloatCast(left); + } +} //========================================================================== // @@ -845,6 +1103,7 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx) } } + Promote(); return this; } @@ -880,6 +1139,72 @@ ExpVal FxAddSub::EvalExpression (AActor *self) return ret; } +ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) +{ + assert(Operator == '+' || Operator == '-'); + ExpEmit op1 = left->Emit(build); + ExpEmit op2 = right->Emit(build); + if (Operator == '+') + { + // Since addition is commutative, only the second operand may be a constant. + if (op1.Konst) + { + swap(op1, op2); + } + assert(!op1.Konst); + build->FreeReg(op1.RegType, op1.RegNum); + if (!op2.Konst) + { + build->FreeReg(op2.RegType, op2.RegNum); + } + if (ValueType == VAL_Float) + { + assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); + ExpEmit to(build, REGT_FLOAT); + build->Emit(op2.Konst ? OP_ADDF_RK : OP_ADDF_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + else + { + assert(ValueType == VAL_Int); + assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); + ExpEmit to(build, REGT_INT); + build->Emit(op2.Konst ? OP_ADD_RK : OP_ADD_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + } + else + { + // Subtraction is not commutative, so either side may be constant (but not both). + assert(!op1.Konst || !op2.Konst); + if (!op1.Konst) + { + build->FreeReg(op1.RegType, op1.RegNum); + } + if (!op2.Konst) + { + build->FreeReg(op2.RegType, op2.RegNum); + } + if (ValueType == VAL_Float) + { + assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); + ExpEmit to(build, REGT_FLOAT); + build->Emit(op1.Konst ? OP_SUBF_KR : op2.Konst ? OP_SUBF_RK : OP_SUBF_RR, + to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + else + { + assert(ValueType == VAL_Int); + assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); + ExpEmit to(build, REGT_INT); + build->Emit(op1.Konst ? OP_SUB_KR : op2.Konst ? OP_SUB_RK : OP_SUB_RR, + to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + } +} + //========================================================================== // // @@ -955,6 +1280,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) } } + Promote(); return this; } @@ -1003,6 +1329,74 @@ ExpVal FxMulDiv::EvalExpression (AActor *self) return ret; } +ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build) +{ + ExpEmit op1 = left->Emit(build); + ExpEmit op2 = right->Emit(build); + + if (Operator == '*') + { + // Multiplication is commutative, so only the second operand may be constant. + if (op1.Konst) + { + swap(op1, op2); + } + assert(!op1.Konst); + if (!op2.Konst) + { + build->FreeReg(op2.RegType, op2.RegNum); + } + if (ValueType == VAL_Float) + { + assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); + ExpEmit to(build, REGT_FLOAT); + build->Emit(op2.Konst ? OP_MULF_RK : OP_MULF_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + else + { + assert(ValueType == VAL_Int); + assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); + ExpEmit to(build, REGT_INT); + build->Emit(op2.Konst ? OP_MUL_RK : OP_MUL_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + } + else + { + // Division is not commutative, so either side may be constant (but not both). + assert(!op1.Konst || !op2.Konst); + assert(Operator == '%' || Operator == '/'); + if (!op1.Konst) + { + build->FreeReg(op1.RegType, op1.RegNum); + } + if (!op2.Konst) + { + build->FreeReg(op2.RegType, op2.RegNum); + } + if (ValueType == VAL_Float) + { + assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); + ExpEmit to(build, REGT_FLOAT); + build->Emit(Operator == '/' ? (op1.Konst ? OP_DIVF_KR : op2.Konst ? OP_DIVF_RK : OP_DIVF_RR) + : (op1.Konst ? OP_MODF_KR : op2.Konst ? OP_MODF_RK : OP_MODF_RR), + to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + else + { + assert(ValueType == VAL_Int); + assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); + ExpEmit to(build, REGT_INT); + build->Emit(Operator == '/' ? (op1.Konst ? OP_DIV_KR : op2.Konst ? OP_DIV_RK : OP_DIV_RR) + : (op1.Konst ? OP_MOD_KR : op2.Konst ? OP_MOD_RK : OP_MOD_RR), + to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + } +} + //========================================================================== // // @@ -1057,6 +1451,7 @@ FxExpression *FxCompareRel::Resolve(FCompileContext& ctx) delete this; return e; } + Promote(); ValueType = VAL_Int; return this; } @@ -1095,6 +1490,53 @@ ExpVal FxCompareRel::EvalExpression (AActor *self) return ret; } +ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build) +{ + ExpEmit op1 = left->Emit(build); + ExpEmit op2 = right->Emit(build); + assert(op1.RegType == op2.RegType); + assert(op1.RegType == REGT_INT || op1.RegType == REGT_FLOAT); + assert(!op1.Konst || !op2.Konst); + assert(Operator == '<' || Operator == '>' || Operator == TK_Geq || Operator == TK_Leq); + static const VM_UBYTE InstrMap[][4] = + { + { OP_LT_RR, OP_LTF_RR, 0 }, // < + { OP_LE_RR, OP_LEF_RR, 1 }, // > + { OP_LT_RR, OP_LTF_RR, 1 }, // >= + { OP_LE_RR, OP_LE_RR, 0 } // <= + }; + int instr, check, index; + + index = Operator == '<' ? 0 : + Operator == '>' ? 1 : + Operator == TK_Geq ? 2 : 3; + instr = InstrMap[index][op1.RegType == REGT_INT ? 0 : 1]; + check = InstrMap[index][2]; + if (op2.Konst) + { + instr += 1; + } + else + { + build->FreeReg(op2.RegType, op2.RegNum); + } + if (op1.Konst) + { + instr += 2; + } + else + { + build->FreeReg(op1.RegType, op1.RegNum); + } + ExpEmit to(build, op1.RegType); + + // See FxUnaryNotBoolean for comments, since it's the same thing. + build->Emit(OP_LI, to.RegNum, 0, 0); + build->Emit(instr, check, op1.RegNum, op2.RegNum); + build->Emit(OP_JMP, 1); + build->Emit(OP_LI, to.RegNum, 1); + return to; +} //========================================================================== // @@ -1162,6 +1604,7 @@ cont: delete this; return e; } + Promote(); ValueType = VAL_Int; return this; } @@ -1184,7 +1627,7 @@ ExpVal FxCompareEq::EvalExpression (AActor *self) double v2 = right->EvalExpression(self).GetFloat(); ret.Int = Operator == TK_Eq? v1 == v2 : v1 != v2; } - else if (ValueType == VAL_Int) + else if (left->ValueType == VAL_Int) { int v1 = left->EvalExpression(self).GetInt(); int v2 = right->EvalExpression(self).GetInt(); @@ -1198,6 +1641,42 @@ ExpVal FxCompareEq::EvalExpression (AActor *self) return ret; } +ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build) +{ + ExpEmit op1 = left->Emit(build); + ExpEmit op2 = right->Emit(build); + assert(op1.RegType == op2.RegType); + assert(op1.RegType == REGT_INT || op1.RegType == REGT_FLOAT || op1.RegType == REGT_POINTER); + int instr; + + // Only the second operand may be constant. + if (op1.Konst) + { + swap(op1, op2); + } + assert(!op1.Konst); + + instr = op1.RegType == REGT_INT ? OP_EQ_R : + op1.RegType == REGT_FLOAT ? OP_EQF_R : + OP_EQA_R; + build->FreeReg(op1.RegType, op1.RegNum); + if (!op2.Konst) + { + build->FreeReg(op2.RegType, op2.RegNum); + } + else + { + instr += 1; + } + ExpEmit to(build, op1.RegType); + + // See FxUnaryNotBoolean for comments, since it's the same thing. + build->Emit(OP_LI, to.RegNum, 0, 0); + build->Emit(instr, Operator != TK_Eq, op1.RegNum, op2.RegNum); + build->Emit(OP_JMP, 1); + build->Emit(OP_LI, to.RegNum, 1); + return to; +} //========================================================================== // @@ -1293,6 +1772,82 @@ ExpVal FxBinaryInt::EvalExpression (AActor *self) return ret; } +ExpEmit FxBinaryInt::Emit(VMFunctionBuilder *build) +{ + assert(left->ValueType == VAL_Int); + assert(right->ValueType == VAL_Int); + static const VM_UBYTE InstrMap[][4] = + { + { OP_SLL_RR, OP_SLL_KR, OP_SLL_RI }, // TK_LShift + { OP_SRA_RR, OP_SRA_KR, OP_SRA_RI }, // TK_RShift + { OP_SRL_RR, OP_SRL_KR, OP_SRL_RI }, // TK_URShift + { OP_AND_RR, 0, OP_AND_RK }, // '&' + { OP_OR_RR, 0, OP_OR_RK }, // '|' + { OP_XOR_RR, 0, OP_XOR_RK }, // '^' + }; + int index, instr, rop; + ExpEmit op1, op2; + + index = Operator == TK_LShift ? 0 : + Operator == TK_RShift ? 1 : + Operator == TK_URShift ? 2 : + Operator == '&' ? 3 : + Operator == '|' ? 4 : + Operator == '^' ? 5 : -1; + assert(index >= 0); + op1 = left->Emit(build); + if (index < 3) + { // Shift instructions use right-hand immediates instead of constant registers. + if (right->isConstant()) + { + rop = right->EvalExpression(NULL).GetInt(); + op2.Konst = true; + } + else + { + op2 = right->Emit(build); + assert(!op2.Konst); + build->FreeReg(op2.RegType, op2.RegNum); + rop = op2.RegNum; + } + } + else + { // The other operators only take a constant on the right-hand side. + op2 = right->Emit(build); + if (op1.Konst) + { + swap(op1, op2); + } + assert(!op1.Konst); + rop = op2.RegNum; + if (!op2.Konst) + { + build->FreeReg(op2.RegType, op2.RegNum); + } + } + if (!op1.Konst) + { + build->FreeReg(op1.RegType, op1.RegNum); + if (!op2.Konst) + { + instr = InstrMap[index][0]; + } + else + { + instr = InstrMap[index][2]; + } + } + else + { + assert(!op2.Konst); + instr = InstrMap[index][1]; + } + assert(instr != 0); + ExpEmit to(build, REGT_INT); + build->Emit(instr, to.RegNum, op1.RegNum, rop); + return to; +} + //========================================================================== // // @@ -1402,6 +1957,14 @@ FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx) return x; } } + if (left->ValueType != VAL_Int && left->ValueType != VAL_Sound) + { + left = new FxIntCast(left); + } + if (right->ValueType != VAL_Int && right->ValueType != VAL_Sound) + { + right = new FxIntCast(right); + } return this; } @@ -1430,6 +1993,59 @@ ExpVal FxBinaryLogical::EvalExpression (AActor *self) return ret; } +ExpEmit FxBinaryLogical::Emit(VMFunctionBuilder *build) +{ + // This is not the "right" way to do these, but it works for now. + // (Problem: No information sharing is done between nodes to reduce the + // code size if you have something like a1 && a2 && a3 && ... && an.) + assert(left->ValueType == VAL_Int && right->ValueType == VAL_Int); + ExpEmit op1 = left->Emit(build); + assert(!op1.Konst); + int zero = build->GetConstantInt(0); + build->FreeReg(op1.RegType, op1.RegNum); + + if (Operator == TK_AndAnd) + { + build->Emit(OP_EQ_K, 1, op1.RegNum, zero); + // If op1 is 0, skip evaluation of op2. + size_t patchspot = build->Emit(OP_JMP, 0, 0, 0); + + // Evaluate op2. + ExpEmit op2 = right->Emit(build); + assert(!op2.Konst); + build->FreeReg(op2.RegType, op2.RegNum); + + ExpEmit to(build, REGT_INT); + build->Emit(OP_EQ_K, 0, op2.RegNum, zero); + build->Emit(OP_JMP, 2); + build->Emit(OP_LI, to.RegNum, 1); + build->Emit(OP_JMP, 1); + size_t target = build->Emit(OP_LI, to.RegNum, 0); + build->Backpatch(patchspot, target); + return to; + } + else + { + assert(Operator == TK_OrOr); + build->Emit(OP_EQ_K, 0, op1.RegNum, zero); + // If op1 is not 0, skip evaluation of op2. + size_t patchspot = build->Emit(OP_JMP, 0, 0, 0); + + // Evaluate op2. + ExpEmit op2 = right->Emit(build); + assert(!op2.Konst); + build->FreeReg(op2.RegType, op2.RegNum); + + ExpEmit to(build, REGT_INT); + build->Emit(OP_EQ_K, 1, op2.RegNum, zero); + build->Emit(OP_JMP, 2); + build->Emit(OP_LI, to.RegNum, 0); + build->Emit(OP_JMP, 1); + size_t target = build->Emit(OP_LI, to.RegNum, 1); + build->Backpatch(patchspot, target); + return to; + } +} //========================================================================== // @@ -1490,6 +2106,20 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx) return e; } + if (ValueType == VAL_Float) + { + if (truex->ValueType != VAL_Float) + { + truex = new FxFloatCast(truex); + RESOLVE(truex, ctx); + } + if (falsex->ValueType != VAL_Float) + { + falsex = new FxFloatCast(falsex); + RESOLVE(falsex, ctx); + } + } + return this; } @@ -1508,6 +2138,78 @@ ExpVal FxConditional::EvalExpression (AActor *self) return e->EvalExpression(self); } +ExpEmit FxConditional::Emit(VMFunctionBuilder *build) +{ + ExpEmit out; + + // The true and false expressions ought to be assigned to the + // same temporary instead of being copied to it. Oh well; good enough + // for now. + ExpEmit cond = condition->Emit(build); + assert(cond.RegType == REGT_INT && !cond.Konst); + + // Test condition. + build->Emit(OP_EQ_K, 1, cond.RegNum, build->GetConstantInt(0)); + size_t patchspot = build->Emit(OP_JMP, 0); + + // Evaluate true expression. + if (truex->isConstant() && truex->ValueType == VAL_Int) + { + out = ExpEmit(build, REGT_INT); + build->EmitLoadInt(out.RegNum, truex->EvalExpression(NULL).GetInt()); + } + else + { + ExpEmit trueop = truex->Emit(build); + if (trueop.Konst) + { + assert(trueop.RegType == REGT_FLOAT); + out = ExpEmit(build, REGT_FLOAT); + build->Emit(OP_LKF, out.RegNum, trueop.RegNum); + } + else + { + // Use the register returned by the true condition as the + // target for the false condition. + out = trueop; + } + } + + // Evaluate false expression. + build->BackpatchToHere(patchspot); + if (falsex->isConstant() && falsex->ValueType == VAL_Int) + { + build->EmitLoadInt(out.RegNum, falsex->EvalExpression(NULL).GetInt()); + } + else + { + ExpEmit falseop = falsex->Emit(build); + if (falseop.Konst) + { + assert(falseop.RegType == REGT_FLOAT); + build->Emit(OP_LKF, out.RegNum, falseop.RegNum); + } + else + { + // Move result from the register returned by "false" to the one + // returned by "true" so that only one register is returned by + // this tree. + build->FreeReg(falseop.RegType, falseop.RegNum); + if (falseop.RegType == REGT_INT) + { + build->Emit(OP_MOVE, out.RegNum, falseop.RegNum, 0); + } + else + { + assert(falseop.RegType == REGT_FLOAT); + build->Emit(OP_MOVEF, out.RegNum, falseop.RegNum, 0); + } + } + } + + return out; +} + //========================================================================== // // @@ -1597,6 +2299,23 @@ ExpVal FxAbs::EvalExpression (AActor *self) return value; } +ExpEmit FxAbs::Emit(VMFunctionBuilder *build) +{ + ExpEmit absofsteal = val->Emit(build); + assert(!absofsteal.Konst); + ExpEmit out(build, absofsteal.RegType); + if (absofsteal.RegType == REGT_INT) + { + build->Emit(OP_ABS, out.RegNum, absofsteal.RegNum, 0); + } + else + { + assert(absofsteal.RegType == REGT_FLOAT); + build->Emit(OP_FLOP, out.RegNum, absofsteal.RegNum, FLOP_ABS); + } + return out; +} + //========================================================================== // // @@ -1676,6 +2395,85 @@ ExpVal FxRandom::EvalExpression (AActor *self) return val; } +int DecoRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + assert(numparam >= 1 && numparam <= 3); + FRandom *rng = reinterpret_cast(param[0].a); + if (numparam == 1) + { + ret->SetInt((*rng)()); + } + else if (numparam == 2) + { + int maskval = param[1].i; + ret->SetInt(rng->Random2(maskval)); + } + else if (numparam == 3) + { + int min = param[1].i, max = param[2].i; + if (max < min) + { + swap(max, min); + } + ret->SetInt((*rng)(max - min + 1) + min); + } + return 1; +} + +ExpEmit FxRandom::Emit(VMFunctionBuilder *build) +{ + // Find the DecoRandom function. If not found, create it and install it + // in Actor. + VMFunction *callfunc; + PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol("DecoRandom", false); + if (sym == NULL) + { + PSymbolVMFunction *symfunc = new PSymbolVMFunction("DecoRandom"); + VMNativeFunction *calldec = new VMNativeFunction(DecoRandom); + symfunc->Function = calldec; + sym = symfunc; + RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); + } + assert(sym->SymbolType == SYM_VMFunction); + assert(((PSymbolVMFunction *)sym)->Function != NULL); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); + if (min != NULL && max != NULL) + { + ExpEmit op = min->Emit(build); + assert(op.RegType == REGT_INT); + if (op.Konst) + { + build->Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, op.RegNum); + } + else + { + build->FreeReg(REGT_INT, op.RegNum); + build->Emit(OP_PARAM, 0, REGT_INT, op.RegNum); + } + op = max->Emit(build); + assert(op.RegType == REGT_INT); + if (op.Konst) + { + build->Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, op.RegNum); + } + else + { + build->FreeReg(REGT_INT, op.RegNum); + build->Emit(OP_PARAM, 0, REGT_INT, op.RegNum); + } + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + } + else + { + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); + } + ExpEmit out(build, REGT_INT); + build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); + return out; +} + //========================================================================== // // @@ -1723,6 +2521,84 @@ ExpVal FxFRandom::EvalExpression (AActor *self) return val; } +int DecoFRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + assert(numparam == 1 || numparam == 3); + FRandom *rng = reinterpret_cast(param[0].a); + + int random = (*rng)(0x40000000); + double frandom = random / double(0x40000000); + + if (numparam == 3) + { + double min = param[1].f, max = param[2].f; + if (max < min) + { + swap(max, min); + } + ret->SetFloat(frandom * (max - min) + min); + } + else + { + ret->SetFloat(frandom); + } + return 1; +} + +ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) +{ + // Find the DecoFRandom function. If not found, create it and install it + // in Actor. + VMFunction *callfunc; + PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol("DecoFRandom", false); + if (sym == NULL) + { + PSymbolVMFunction *symfunc = new PSymbolVMFunction("DecoFRandom"); + VMNativeFunction *calldec = new VMNativeFunction(DecoFRandom); + symfunc->Function = calldec; + sym = symfunc; + RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); + } + assert(sym->SymbolType == SYM_VMFunction); + assert(((PSymbolVMFunction *)sym)->Function != NULL); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); + if (min != NULL && max != NULL) + { + ExpEmit op = min->Emit(build); + assert(op.RegType == REGT_FLOAT); + if (op.Konst) + { + build->Emit(OP_PARAM, 0, REGT_FLOAT | REGT_KONST, op.RegNum); + } + else + { + build->FreeReg(REGT_FLOAT, op.RegNum); + build->Emit(OP_PARAM, 0, REGT_FLOAT, op.RegNum); + } + op = max->Emit(build); + assert(op.RegType == REGT_FLOAT); + if (op.Konst) + { + build->Emit(OP_PARAM, 0, REGT_FLOAT | REGT_KONST, op.RegNum); + } + else + { + build->FreeReg(REGT_FLOAT, op.RegNum); + build->Emit(OP_PARAM, 0, REGT_FLOAT, op.RegNum); + } + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + } + else + { + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); + } + ExpEmit out(build, REGT_FLOAT); + build->Emit(OP_RESULT, 0, REGT_FLOAT, out.RegNum); + return out; +} + //========================================================================== // // @@ -1778,6 +2654,42 @@ ExpVal FxRandom2::EvalExpression (AActor *self) return maskval; } +ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) +{ + // Find the DecoRandom function. If not found, create it and install it + // in Actor. + VMFunction *callfunc; + PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol("DecoRandom", false); + if (sym == NULL) + { + PSymbolVMFunction *symfunc = new PSymbolVMFunction("DecoRandom"); + VMNativeFunction *calldec = new VMNativeFunction(DecoRandom); + symfunc->Function = calldec; + sym = symfunc; + RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); + } + assert(sym->SymbolType == SYM_VMFunction); + assert(((PSymbolVMFunction *)sym)->Function != NULL); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); + ExpEmit op = mask->Emit(build); + assert(op.RegType == REGT_INT); + if (op.Konst) + { + build->Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, op.RegNum); + } + else + { + build->FreeReg(REGT_INT, op.RegNum); + build->Emit(OP_PARAM, 0, REGT_INT, op.RegNum); + } + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + ExpEmit out(build, REGT_INT); + build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); + return out; +} + //========================================================================== // // @@ -1861,7 +2773,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) newex = new FxCVar(cv, ScriptPosition); } */ - // amd line specials + // and line specials else if ((num = P_FindLineSpecial(Identifier, NULL, NULL))) { ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as line special %d\n", Identifier.GetChars(), num); @@ -1924,6 +2836,12 @@ ExpVal FxSelf::EvalExpression (AActor *self) return ret; } +ExpEmit FxSelf::Emit(VMFunctionBuilder *build) +{ + // self is always the first pointer passed to the function; + return ExpEmit(0, REGT_POINTER); +} + //========================================================================== // // @@ -2004,7 +2922,6 @@ ExpVal FxGlobalVariable::EvalExpression (AActor *self) return ret; } - //========================================================================== // // @@ -2122,6 +3039,90 @@ ExpVal FxClassMember::EvalExpression (AActor *self) return ret; } +ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) +{ + ExpEmit obj = classx->Emit(build); + assert(obj.RegType == REGT_POINTER); + + if (AddressRequested) + { + if (membervar->offset == 0) + { + return obj; + } + if (!obj.Konst) + { + build->FreeReg(obj.RegType, obj.RegNum); + } + ExpEmit out(build, REGT_POINTER); + build->Emit(OP_ADDA_RK, out.RegNum, obj.RegNum, build->GetConstantInt((int)membervar->offset)); + return out; + } + + int offsetreg = build->GetConstantInt((int)membervar->offset); + ExpEmit loc, tmp; + + if (obj.Konst) + { + // If the situation where we are dereferencing a constant + // pointer is common, then it would probably be worthwhile + // to add new opcodes for those. But as of right now, I + // don't expect it to be a particularly common case. + ExpEmit newobj(build, REGT_POINTER); + build->Emit(OP_LKP, newobj.RegNum, obj.RegNum); + obj = newobj; + } + + switch (membervar->ValueType.Type) + { + case VAL_Int: + case VAL_Sound: + case VAL_Name: + case VAL_Color: + loc = ExpEmit(build, REGT_INT); + build->Emit(OP_LW, loc.RegNum, obj.RegNum, offsetreg); + break; + + case VAL_Bool: + loc = ExpEmit(build, REGT_INT); + // Some implementations have 1 byte bools, and others have + // 4 byte bools. For all I know, there might be some with + // 2 byte bools, too. + build->Emit((sizeof(bool) == 1 ? OP_LBU : sizeof(bool) == 2 ? OP_LHU : OP_LW), + loc.RegNum, obj.RegNum, offsetreg); + break; + + case VAL_Float: + loc = ExpEmit(build, REGT_FLOAT); + build->Emit(OP_LDP, loc.RegNum, obj.RegNum, offsetreg); + break; + + case VAL_Fixed: + loc = ExpEmit(build, REGT_FLOAT); + build->Emit(OP_LX, loc.RegNum, obj.RegNum, offsetreg); + break; + + case VAL_Angle: + loc = ExpEmit(build, REGT_FLOAT); + tmp = ExpEmit(build, REGT_INT); + build->Emit(OP_LW, tmp.RegNum, obj.RegNum, offsetreg); + build->Emit(OP_CAST, loc.RegNum, tmp.RegNum, CAST_I2F); + build->Emit(OP_MULF_RK, loc.RegNum, loc.RegNum, build->GetConstantFloat(90.0 / ANGLE_90)); + build->FreeReg(tmp.RegType, tmp.RegNum); + break; + + case VAL_Object: + case VAL_Class: + loc = ExpEmit(build, REGT_POINTER); + build->Emit(OP_LO, loc.RegNum, obj.RegNum, offsetreg); + break; + + default: + assert(0); + } + build->FreeReg(obj.RegType, obj.RegNum); + return loc; +} //========================================================================== @@ -2235,6 +3236,37 @@ ExpVal FxArrayElement::EvalExpression (AActor *self) return ret; } +ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) +{ + ExpEmit start = Array->Emit(build); + ExpEmit dest(build, REGT_INT); + if (start.Konst) + { + ExpEmit tmpstart(build, REGT_POINTER); + build->Emit(OP_LKP, tmpstart.RegNum, start.RegNum); + start = tmpstart; + } + if (index->isConstant()) + { + int indexval = index->EvalExpression(NULL).GetInt(); + if (indexval < 0 || indexval >= Array->ValueType.size) + { + I_Error("Array index out of bounds"); + } + indexval <<= 2; + build->Emit(OP_LW, dest.RegNum, start.RegNum, build->GetConstantInt(indexval)); + } + else + { + ExpEmit indexv(index->Emit(build)); + build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, 2); + build->Emit(OP_BOUND, indexv.RegNum, Array->ValueType.size); + build->Emit(OP_LW_R, dest.RegNum, start.RegNum, indexv.RegNum); + build->FreeReg(indexv.RegType, indexv.RegNum); + } + build->FreeReg(start.RegType, start.RegNum); + return dest; +} //========================================================================== // @@ -2412,6 +3444,64 @@ ExpVal FxActionSpecialCall::EvalExpression (AActor *self) return ret; } +int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + assert(numparam > 2 && numparam < 7); + assert(numret == 1); + assert(param[0].Type == REGT_INT); + assert(param[1].Type == REGT_POINTER); + int v[5] = { 0 }; + + for (int i = 2; i < numparam; ++i) + { + v[i - 2] = param[i].i; + } + ret->SetInt(LineSpecials[param[0].i](NULL, reinterpret_cast(param[1].a), false, v[0], v[1], v[2], v[3], v[4])); + return 1; +} + +ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) +{ + assert(Self == NULL); + unsigned i = 0; + + build->Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, build->GetConstantInt(Special)); // pass special number + build->Emit(OP_PARAM, 0, REGT_POINTER, 0); // pass self + if (ArgList != NULL) + { + for (; i < ArgList->Size(); ++i) + { + ExpEmit arg((*ArgList)[i]->Emit(build)); + assert(arg.RegType == REGT_INT); + build->Emit(OP_PARAM, 0, arg.RegType | (arg.Konst ? REGT_KONST : 0), arg.RegNum); + if (!arg.Konst) + { + build->FreeReg(arg.RegType, arg.RegNum); + } + } + } + // Find the DecoCallLineSpecial function. If not found, create it and install it + // in Actor. + VMFunction *callfunc; + PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol("DecoCallLineSpecial", false); + if (sym == NULL) + { + PSymbolVMFunction *symfunc = new PSymbolVMFunction("DecoCallLineSpecial"); + VMNativeFunction *calldec = new VMNativeFunction(DecoCallLineSpecial); + symfunc->Function = calldec; + sym = symfunc; + RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); + } + assert(sym->SymbolType == SYM_VMFunction); + assert(((PSymbolVMFunction *)sym)->Function != NULL); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + ExpEmit dest(build, REGT_INT); + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 1); + build->Emit(OP_RESULT, 0, REGT_INT, dest.RegNum); + return dest; +} + //========================================================================== // // @@ -2466,6 +3556,19 @@ FxExpression *FxGlobalFunctionCall::Resolve(FCompileContext& ctx) delete this; return NULL; } + if ((*ArgList)[0]->isConstant()) + { + double v = (*ArgList)[0]->EvalExpression(NULL).GetFloat(); + v *= M_PI / 180.0; // convert from degrees to radians + v = (Name == NAME_Sin) ? sin(v) : cos(v); + FxExpression *x = new FxConstant(v, ScriptPosition); + delete this; + return x; + } + if ((*ArgList)[0]->ValueType == VAL_Int) + { + (*ArgList)[0] = new FxFloatCast((*ArgList)[0]); + } ValueType = VAL_Float; return this; } @@ -2490,6 +3593,15 @@ ExpVal FxGlobalFunctionCall::EvalExpression (AActor *self) return ret; } +ExpEmit FxGlobalFunctionCall::Emit(VMFunctionBuilder *build) +{ + ExpEmit v = (*ArgList)[0]->Emit(build); + assert(!v.Konst && v.RegType == REGT_FLOAT); + + build->Emit(OP_MULF_RK, v.RegNum, v.RegNum, build->GetConstantFloat(M_PI / 180.0)); + build->Emit(OP_FLOP, v.RegNum, v.RegNum, (Name == NAME_Sin) ? FLOP_SIN : FLOP_COS); + return v; +} //========================================================================== // @@ -2595,6 +3707,60 @@ ExpVal FxClassTypeCast::EvalExpression (AActor *self) return ret; } +int DecoNameToClass(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + assert(numparam == 2); + assert(numret == 1); + assert(param[0].Type == REGT_INT); + assert(param[1].Type == REGT_POINTER); + assert(ret->RegType == REGT_POINTER); + + FName clsname = ENamedName(param[0].i); + const PClass *cls = PClass::FindClass(clsname); + const PClass *desttype = reinterpret_cast(param[0].a); + + if (!cls->IsDescendantOf(desttype)) + { + Printf("class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); + cls = NULL; + } + ret->SetPointer(const_cast(cls)); + return 1; +} + +ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) +{ + if (basex->ValueType != VAL_Name) + { + return ExpEmit(build->GetConstantAddress(NULL, ATAG_OBJECT), REGT_POINTER, true); + } + ExpEmit clsname = basex->Emit(build); + assert(!clsname.Konst); + ExpEmit dest(build, REGT_POINTER); + build->Emit(OP_PARAM, 0, clsname.RegType, clsname.RegNum); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(const_cast(desttype), ATAG_OBJECT)); + + // Find the DecoNameToClass function. If not found, create it and install it + // in Actor. + VMFunction *callfunc; + PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol("DecoNameToClass", false); + if (sym == NULL) + { + PSymbolVMFunction *symfunc = new PSymbolVMFunction("DecoNameToClass"); + VMNativeFunction *calldec = new VMNativeFunction(DecoNameToClass); + symfunc->Function = calldec; + sym = symfunc; + RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); + } + assert(sym->SymbolType == SYM_VMFunction); + assert(((PSymbolVMFunction *)sym)->Function != NULL); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum); + build->FreeReg(clsname.RegType, clsname.RegNum); + return dest; +} //========================================================================== // @@ -2742,6 +3908,64 @@ ExpVal FxMultiNameState::EvalExpression (AActor *self) return ret; } +int DecoFindMultiNameState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + assert(numparam > 1); + assert(numret == 1); + assert(param[0].Type == REGT_POINTER); + assert(ret->RegType == REGT_POINTER); + + FName *names = (FName *)alloca((numparam - 1) * sizeof(FName)); + for (int i = 1; i < numparam; ++i) + { + names[i - 1] = ENamedName(param[i].i); + } + AActor *self = reinterpret_cast(param[0].a); + FState *state = self->GetClass()->ActorInfo->FindState(numparam - 1, names); + if (state == NULL) + { + const char *dot = ""; + Printf("Jump target '"); + for (int i = 0; i < numparam - 1; i++) + { + Printf("%s%s", dot, names[i].GetChars()); + dot = "."; + } + Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars()); + } + ret->SetPointer(state); + return 1; +} + +ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build) +{ + ExpEmit dest(build, REGT_POINTER); + build->Emit(OP_PARAM, 0, REGT_POINTER, 0); // pass self + for (unsigned i = 0; i < names.Size(); ++i) + { + build->Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, build->GetConstantInt(names[i])); + } + + // Find the DecoFindMultiNameState function. If not found, create it and install it + // in Actor. + VMFunction *callfunc; + PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol("DecoFindMultiNameState", false); + if (sym == NULL) + { + PSymbolVMFunction *symfunc = new PSymbolVMFunction("DecoFindMultiNameState"); + VMNativeFunction *calldec = new VMNativeFunction(DecoFindMultiNameState); + symfunc->Function = calldec; + sym = symfunc; + RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); + } + assert(sym->SymbolType == SYM_VMFunction); + assert(((PSymbolVMFunction *)sym)->Function != NULL); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), names.Size() + 1, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum); + return dest; +} //========================================================================== @@ -2903,4 +4127,3 @@ FxExpression *FStateExpressions::Get(int num) return expressions[num].expr; return NULL; } - diff --git a/src/zscript/vm.h b/src/zscript/vm.h index 156f7787a..1436977da 100644 --- a/src/zscript/vm.h +++ b/src/zscript/vm.h @@ -107,6 +107,7 @@ enum ATAG_AREGISTER, // pointer to an address register ATAG_STATE, // pointer to FState + ATAG_RNG, // pointer to FRandom }; class VMFunction : public DObject diff --git a/src/zscript/vmbuilder.cpp b/src/zscript/vmbuilder.cpp index d700b9908..fd0119055 100644 --- a/src/zscript/vmbuilder.cpp +++ b/src/zscript/vmbuilder.cpp @@ -61,10 +61,10 @@ VMScriptFunction *VMFunctionBuilder::MakeFunction() } // Assign required register space. - func->NumRegD = IntRegisters.MostUsed; - func->NumRegF = FloatRegisters.MostUsed; - func->NumRegA = AddressRegisters.MostUsed; - func->NumRegS = StringRegisters.MostUsed; + func->NumRegD = Registers[REGT_INT].MostUsed; + func->NumRegF = Registers[REGT_FLOAT].MostUsed; + func->NumRegA = Registers[REGT_POINTER].MostUsed; + func->NumRegS = Registers[REGT_STRING].MostUsed; func->MaxParam = MaxParam; // Technically, there's no reason why we can't end the function with @@ -274,7 +274,7 @@ VMFunctionBuilder::RegAvailability::RegAvailability() //========================================================================== // -// VMFunctionBuilder :: RegAvailibity :: GetReg +// VMFunctionBuilder :: RegAvailibity :: Get // // Gets one or more unused registers. If getting multiple registers, they // will all be consecutive. Returns -1 if there were not enough consecutive @@ -286,7 +286,7 @@ VMFunctionBuilder::RegAvailability::RegAvailability() // //========================================================================== -int VMFunctionBuilder::RegAvailability::GetReg(int count) +int VMFunctionBuilder::RegAvailability::Get(int count) { VM_UWORD mask; int i, firstbit; @@ -368,13 +368,13 @@ int VMFunctionBuilder::RegAvailability::GetReg(int count) //========================================================================== // -// VMFunctionBuilder :: RegAvailibity :: ReturnReg +// VMFunctionBuilder :: RegAvailibity :: Return // // Marks a range of registers as free again. // //========================================================================== -void VMFunctionBuilder::RegAvailability::ReturnReg(int reg, int count) +void VMFunctionBuilder::RegAvailability::Return(int reg, int count) { assert(count >= 1 && count <= 32); assert(reg >= 0 && reg + count <= 256); @@ -406,8 +406,108 @@ void VMFunctionBuilder::RegAvailability::ReturnReg(int reg, int count) } } -void VMFunctionBuilder::RegAvailability::Dump() +//========================================================================== +// +// VMFunctionBuilder :: Emit +// +// Just dumbly output an instruction. Returns instruction position, not +// byte position. (Because all instructions are exactly four bytes long.) +// +//========================================================================== + +size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc) { - Printf("%032B %032B %032B %032B\n%032B %032B %032B %032B\n", - Used[0], Used[1], Used[2], Used[3], Used[4], Used[5], Used[6], Used[7]); + assert(opcode >= 0 && opcode < NUM_OPS); + assert(opa >= 0 && opa <= 255); + assert(opb >= 0 && opb <= 255); + assert(opc >= 0 && opc <= 255); + size_t loc = Code.Reserve(4); + VM_UBYTE *code = &Code[loc]; + code[0] = opcode; + code[1] = opa; + code[2] = opb; + code[3] = opc; + return loc / 4; +} + +size_t VMFunctionBuilder::Emit(int opcode, int opa, VM_SHALF opbc) +{ + assert(opcode >= 0 && opcode < NUM_OPS); + assert(opa >= 0 && opa <= 255); + assert(opbc >= -32768 && opbc <= 32767); + size_t loc = Code.Reserve(4); + VM_UBYTE *code = &Code[loc]; + code[0] = opcode; + code[1] = opa; + *(VM_SHALF *)&code[2] = opbc; + return loc / 4; +} + +size_t VMFunctionBuilder::Emit(int opcode, int opabc) +{ + assert(opcode >= 0 && opcode < NUM_OPS); + assert(opabc >= -(1 << 23) && opabc <= (1 << 24) - 1); + size_t loc = Code.Reserve(4); +#ifdef __BIG_ENDIAN__ + *(VM_UWORD *)&Code[loc] = (opabc & 0xFFFFFF) | (opcode << 24); +#else + *(VM_UWORD *)&Code[loc] = opcode | (opabc << 8); +#endif + return loc / 4; +} + +//========================================================================== +// +// VMFunctionBuilder :: EmitLoadInt +// +// Loads an integer constant into a register, using either an immediate +// value or a constant register, as appropriate. +// +//========================================================================== + +size_t VMFunctionBuilder::EmitLoadInt(int regnum, int value) +{ + assert(regnum >= 0 && regnum < Registers[REGT_INT].MostUsed); + if (value >= -32768 && value <= 32767) + { + return Emit(OP_LI, regnum, value); + } + else + { + return Emit(OP_LK, regnum, GetConstantInt(value)); + } +} + +//========================================================================== +// +// VMFunctionBuilder :: Backpatch +// +// Store a JMP instruction at that points at . +// +//========================================================================== + +void VMFunctionBuilder::Backpatch(size_t loc, size_t target) +{ + assert(loc < Code.Size() / 4); + int offset = int(target - loc - 1); + assert(offset >= -(1 << 24) && offset <= (1 << 24) - 1); +#ifdef __BIG_ENDIAN__ + *(VM_UWORD *)&Code[loc * 4] = (offset & 0xFFFFFF) | (OP_JMP << 24); +#else + *(VM_UWORD *)&Code[loc * 4] = OP_JMP | (offset << 8); +#endif +} + +//========================================================================== +// +// VMFunctionBuilder :: BackpatchToHere +// +// Store a JMP instruction at that points to the current code gen +// location. +// +//========================================================================== + +void VMFunctionBuilder::BackpatchToHere(size_t loc) +{ + Backpatch(loc, Code.Size() / 4); } diff --git a/src/zscript/vmbuilder.h b/src/zscript/vmbuilder.h index 95d6f9dd3..af43cbf8b 100644 --- a/src/zscript/vmbuilder.h +++ b/src/zscript/vmbuilder.h @@ -11,9 +11,8 @@ public: { public: RegAvailability(); - int GetReg(int count); // Returns the first register in the range - void ReturnReg(int reg, int count); - void Dump(); + int Get(int count); // Returns the first register in the range + void Return(int reg, int count); private: VM_UWORD Used[256/32]; // Bitmap of used registers (bit set means reg is used) @@ -35,6 +34,12 @@ public: // Returns the address of the newly-emitted instruction. size_t Emit(int opcode, int opa, int opb, int opc); + size_t Emit(int opcode, int opa, VM_SHALF opbc); + size_t Emit(int opcode, int opabc); + size_t EmitLoadInt(int regnum, int value); + + void Backpatch(size_t addr, size_t target); + void BackpatchToHere(size_t addr); // Write out complete constant tables. void FillIntConstants(int *konst); @@ -46,10 +51,10 @@ public: void ParamChange(int delta); // Track available registers. - RegAvailability IntRegisters; - RegAvailability FloatRegisters; - RegAvailability AddressRegisters; - RegAvailability StringRegisters; + RegAvailability Registers[4]; + + // Free a register. + void FreeReg(int regtype, int regnum); private: struct AddrKonst diff --git a/src/zscript/vmexec.cpp b/src/zscript/vmexec.cpp index 9c32b4fc5..8835afcc8 100644 --- a/src/zscript/vmexec.cpp +++ b/src/zscript/vmexec.cpp @@ -30,8 +30,8 @@ #define JMPOFS(x) ((*(VM_SWORD *)(x) >> 6) & ~3) #endif -#define KC (konst[C]) -#define RC (reg.i[C]) +#define KC (konstd[C]) +#define RC (reg.d[C]) #define PA (reg.a[A]) #define PB (reg.a[B]) @@ -61,11 +61,12 @@ enum X_READ_NIL, X_WRITE_NIL, X_TOO_MANY_TRIES, + X_ARRAY_OUT_OF_BOUNDS }; #define GETADDR(a,o,x) \ - if (a == 0) { THROW(x); } \ - ptr = (VM_SBYTE *)a + x \ + if (a == NULL) { THROW(x); } \ + ptr = (VM_SBYTE *)a + o static const VM_UWORD ZapTable[16] = { diff --git a/src/zscript/vmexec.h b/src/zscript/vmexec.h index 350845e5d..e300e6644 100644 --- a/src/zscript/vmexec.h +++ b/src/zscript/vmexec.h @@ -586,6 +586,13 @@ begin: assert(0); NEXTOP; + OP(BOUND): + if (reg.d[a] >= BC) + { + THROW(X_ARRAY_OUT_OF_BOUNDS); + } + NEXTOP; + OP(CONCAT): ASSERTS(a); ASSERTS(B); ASSERTS(C); { diff --git a/src/zscript/vmops.h b/src/zscript/vmops.h index f7d821bb9..00c6f5d13 100644 --- a/src/zscript/vmops.h +++ b/src/zscript/vmops.h @@ -73,7 +73,7 @@ xx(DYNCAST_K, dyncast,RPRPKP), xx(TEST, test, RII16), // if (dA != BC) then pc++ xx(JMP, jmp, I24), // pc += ABC -- The ABC fields contain a signed 24-bit offset. xx(IJMP, ijmp, RII16), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP. -xx(PARAM, param, __BCP), // push parameter encoded in BC for function call or result for return +xx(PARAM, param, __BCP), // push parameter encoded in BC for function call (B=regtype, C=regnum) xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C xx(CALL_K, call, KPI8I8), xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL) @@ -87,6 +87,7 @@ xx(CATCH, catch, CATCH), // A == 0: continue search on next try // A == 2: (pB == ) then pc++ ; next instruction must JMP to another CATCH // A == 3: (pkB == ) then pc++ ; next instruction must JMP to another CATCH // for A > 0, exception is stored in pC +xx(BOUND, bound, RII16), // if rA >= BC, throw exception // String instructions. xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC @@ -129,7 +130,7 @@ xx(MAX_RR, max, RIRIRI), // dA = max(dB,dkC) xx(MAX_RK, max, RIRIKI), xx(ABS, abs, RIRI), // dA = abs(dB) xx(NEG, neg, RIRI), // dA = -dB -xx(NOT, not, RIRI), // dA = !dB +xx(NOT, not, RIRI), // dA = ~dB xx(SEXT, sext, RIRII8), // dA = dB, sign extended by shifting left then right by C xx(ZAP_R, zap, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are one xx(ZAP_I, zap, RIRII8),