/* ** thingdef_expression.cpp ** ** Expression evaluation ** **--------------------------------------------------------------------------- ** Copyright 2008 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be ** covered by the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or (at ** your option) any later version. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include #include "actor.h" #include "sc_man.h" #include "tarray.h" #include "templates.h" #include "cmdlib.h" #include "i_system.h" #include "m_random.h" #include "a_pickups.h" #include "thingdef.h" #include "p_lnspec.h" #include "doomstat.h" #include "thingdef_exp.h" #include "m_fixed.h" #include "vmbuilder.h" #include "v_text.h" struct FLOP { ENamedName Name; int Flop; double (*Evaluate)(double); }; // Decorate operates on degrees, so the evaluate functions need to convert // degrees to radians for those that work with angles. static const FLOP FxFlops[] = { { NAME_Exp, FLOP_EXP, [](double v) { return exp(v); } }, { NAME_Log, FLOP_LOG, [](double v) { return log(v); } }, { NAME_Log10, FLOP_LOG10, [](double v) { return log10(v); } }, { NAME_Sqrt, FLOP_SQRT, [](double v) { return sqrt(v); } }, { NAME_Ceil, FLOP_CEIL, [](double v) { return ceil(v); } }, { NAME_Floor, FLOP_FLOOR, [](double v) { return floor(v); } }, { NAME_ACos, FLOP_ACOS_DEG, [](double v) { return acos(v) * (180.0 / M_PI); } }, { NAME_ASin, FLOP_ASIN_DEG, [](double v) { return asin(v) * (180.0 / M_PI); } }, { NAME_ATan, FLOP_ATAN_DEG, [](double v) { return atan(v) * (180.0 / M_PI); } }, { NAME_Cos, FLOP_COS_DEG, [](double v) { return cos(v * (M_PI / 180.0)); } }, { NAME_Sin, FLOP_SIN_DEG, [](double v) { return sin(v * (M_PI / 180.0)); } }, { NAME_Tan, FLOP_TAN_DEG, [](double v) { return tan(v * (M_PI / 180.0)); } }, { NAME_CosH, FLOP_COSH, [](double v) { return cosh(v); } }, { NAME_SinH, FLOP_SINH, [](double v) { return sinh(v); } }, { NAME_TanH, FLOP_TANH, [](double v) { return tanh(v); } }, }; ExpEmit::ExpEmit(VMFunctionBuilder *build, int type) : RegNum(build->Registers[type].Get(1)), RegType(type), Konst(false), Fixed(false) { } void ExpEmit::Free(VMFunctionBuilder *build) { if (!Fixed && !Konst && RegType <= REGT_TYPE) { build->Registers[RegType].Return(RegNum, 1); } } void ExpEmit::Reuse(VMFunctionBuilder *build) { if (!Fixed && !Konst) { bool success = build->Registers[RegType].Reuse(RegNum); assert(success && "Attempt to reuse a register that is already in use"); } } //========================================================================== // // FindDecorateBuiltinFunction // // Returns the symbol for a decorate utility function. If not found, create // it and install it in Actor. // //========================================================================== static PSymbol *FindDecorateBuiltinFunction(FName funcname, VMNativeFunction::NativeCallType func) { PSymbol *sym = RUNTIME_CLASS(AActor)->Symbols.FindSymbol(funcname, false); if (sym == NULL) { PSymbolVMFunction *symfunc = new PSymbolVMFunction(funcname); VMNativeFunction *calldec = new VMNativeFunction(func, funcname); symfunc->Function = calldec; sym = symfunc; RUNTIME_CLASS(AActor)->Symbols.AddSymbol(sym); } return sym; } //========================================================================== // // // //========================================================================== ExpEmit FxExpression::Emit (VMFunctionBuilder *build) { ScriptPosition.Message(MSG_ERROR, "Unemitted expression found"); return ExpEmit(); } //========================================================================== // // // //========================================================================== bool FxExpression::isConstant() const { return false; } //========================================================================== // // // //========================================================================== VMFunction *FxExpression::GetDirectFunction() { return NULL; } //========================================================================== // // // //========================================================================== FxExpression *FxExpression::Resolve(FCompileContext &ctx) { isresolved = true; return this; } //========================================================================== // // // //========================================================================== FxExpression *FxExpression::ResolveAsBoolean(FCompileContext &ctx) { ///FIXME: Use an actual boolean type FxExpression *x = Resolve(ctx); if (x != NULL) { switch (x->ValueType.Type) { case VAL_Int: case VAL_Sound: case VAL_Color: case VAL_Name: x->ValueType = VAL_Int; break; case VAL_State: x = new FxCastStateToBool(x); x = x->Resolve(ctx); break; default: ScriptPosition.Message(MSG_ERROR, "Not an integral type"); delete this; return NULL; break; } } return x; } //========================================================================== // // // //========================================================================== void FxExpression::RequestAddress() { ScriptPosition.Message(MSG_ERROR, "invalid dereference\n"); } //========================================================================== // // // //========================================================================== FxParameter::FxParameter(FxExpression *operand) : FxExpression(operand->ScriptPosition) { Operand = operand; ValueType = operand->ValueType; } //========================================================================== // // // //========================================================================== FxParameter::~FxParameter() { SAFE_DELETE(Operand); } //========================================================================== // // // //========================================================================== FxExpression *FxParameter::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); SAFE_RESOLVE(Operand, ctx); ValueType = Operand->ValueType; return this; } ExpEmit FxParameter::Emit(VMFunctionBuilder *build) { if (Operand->isConstant()) { ExpVal val = static_cast(Operand)->GetValue(); if (val.Type == VAL_Int || val.Type == VAL_Sound || val.Type == VAL_Name || val.Type == VAL_Color) { build->EmitParamInt(val.Int); } else if (val.Type == VAL_Float) { build->Emit(OP_PARAM, 0, REGT_FLOAT | REGT_KONST, build->GetConstantFloat(val.Float)); } 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 if (val.Type == VAL_String) { build->Emit(OP_PARAM, 0, REGT_STRING | REGT_KONST, build->GetConstantString(val.GetString())); } else { build->Emit(OP_PARAM, 0, REGT_NIL, 0); ScriptPosition.Message(MSG_ERROR, "Cannot emit needed constant"); } } else { ExpEmit where = Operand->Emit(build); if (where.RegType == REGT_NIL) { ScriptPosition.Message(MSG_ERROR, "Attempted to pass a non-value"); build->Emit(OP_PARAM, 0, where.RegType, where.RegNum); } else { build->Emit(OP_PARAM, 0, where.RegType, where.RegNum); where.Free(build); } } return ExpEmit(); } //========================================================================== // // // //========================================================================== FxExpression *FxConstant::MakeConstant(PSymbol *sym, const FScriptPosition &pos) { FxExpression *x; PSymbolConstNumeric *csym = dyn_cast(sym); if (csym != NULL) { if (csym->ValueType->IsA(RUNTIME_CLASS(PInt))) { x = new FxConstant(csym->Value, pos); } else if (csym->ValueType->IsA(RUNTIME_CLASS(PFloat))) { x = new FxConstant(csym->Float, pos); } else { pos.Message(MSG_ERROR, "Invalid constant '%s'\n", csym->SymbolName.GetChars()); return NULL; } } else { pos.Message(MSG_ERROR, "'%s' is not a constant\n", sym->SymbolName.GetChars()); x = NULL; } return x; } ExpEmit FxConstant::Emit(VMFunctionBuilder *build) { ExpEmit out; out.Konst = true; if (value.Type == VAL_Int || value.Type == VAL_Sound || value.Type == VAL_Name || value.Type == VAL_Color) { out.RegType = REGT_INT; out.RegNum = build->GetConstantInt(value.Int); } else if (value.Type == VAL_Float) { out.RegType = REGT_FLOAT; out.RegNum = build->GetConstantFloat(value.Float); } else if (value.Type == VAL_Class || value.Type == VAL_Object) { out.RegType = REGT_POINTER; out.RegNum = build->GetConstantAddress(value.pointer, ATAG_OBJECT); } else if (value.Type == VAL_State) { out.RegType = REGT_POINTER; out.RegNum = build->GetConstantAddress(value.pointer, ATAG_STATE); } else if (value.Type == VAL_String) { out.RegType = REGT_STRING; out.RegNum = build->GetConstantString(value.GetString()); } else { ScriptPosition.Message(MSG_ERROR, "Cannot emit needed constant"); out.RegType = REGT_NIL; out.RegNum = 0; } return out; } //========================================================================== // // // //========================================================================== FxIntCast::FxIntCast(FxExpression *x) : FxExpression(x->ScriptPosition) { basex=x; ValueType = VAL_Int; } //========================================================================== // // // //========================================================================== FxIntCast::~FxIntCast() { SAFE_DELETE(basex); } //========================================================================== // // // //========================================================================== FxExpression *FxIntCast::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(basex, ctx); if (basex->ValueType == VAL_Int) { FxExpression *x = basex; basex = NULL; delete this; return x; } else if (basex->ValueType == VAL_Float) { if (basex->isConstant()) { ExpVal constval = static_cast(basex)->GetValue(); FxExpression *x = new FxConstant(constval.GetInt(), ScriptPosition); delete this; return x; } return this; } else { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } } //========================================================================== // // // //========================================================================== ExpEmit FxIntCast::Emit(VMFunctionBuilder *build) { ExpEmit from = basex->Emit(build); assert(!from.Konst); assert(basex->ValueType == VAL_Float); from.Free(build); 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 = static_cast(basex)->GetValue(); 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; } } //========================================================================== // // // //========================================================================== ExpEmit FxFloatCast::Emit(VMFunctionBuilder *build) { ExpEmit from = basex->Emit(build); assert(!from.Konst); assert(basex->ValueType == VAL_Int); from.Free(build); ExpEmit to(build, REGT_FLOAT); build->Emit(OP_CAST, to.RegNum, from.RegNum, CAST_I2F); return to; } //========================================================================== // // // //========================================================================== FxCastStateToBool::FxCastStateToBool(FxExpression *x) : FxExpression(x->ScriptPosition) { basex = x; ValueType = VAL_Int; } //========================================================================== // // // //========================================================================== FxCastStateToBool::~FxCastStateToBool() { SAFE_DELETE(basex); } //========================================================================== // // // //========================================================================== FxExpression *FxCastStateToBool::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(basex, ctx); assert(basex->ValueType == VAL_State); assert(!basex->isConstant() && "We shouldn't be able to generate a constant state ref"); return this; } //========================================================================== // // // //========================================================================== ExpEmit FxCastStateToBool::Emit(VMFunctionBuilder *build) { ExpEmit from = basex->Emit(build); assert(from.RegType == REGT_POINTER); from.Free(build); ExpEmit to(build, REGT_INT); // If from is NULL, produce 0. Otherwise, produce 1. build->Emit(OP_LI, to.RegNum, 0); build->Emit(OP_EQA_K, 1, from.RegNum, build->GetConstantAddress(NULL, ATAG_GENERIC)); build->Emit(OP_JMP, 1); build->Emit(OP_LI, to.RegNum, 1); return to; } //========================================================================== // // // //========================================================================== FxPlusSign::FxPlusSign(FxExpression *operand) : FxExpression(operand->ScriptPosition) { Operand=operand; } //========================================================================== // // // //========================================================================== FxPlusSign::~FxPlusSign() { SAFE_DELETE(Operand); } //========================================================================== // // // //========================================================================== FxExpression *FxPlusSign::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); SAFE_RESOLVE(Operand, ctx); if (Operand->ValueType.isNumeric()) { FxExpression *e = Operand; Operand = NULL; delete this; return e; } else { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } } ExpEmit FxPlusSign::Emit(VMFunctionBuilder *build) { return Operand->Emit(build); } //========================================================================== // // // //========================================================================== FxMinusSign::FxMinusSign(FxExpression *operand) : FxExpression(operand->ScriptPosition) { Operand=operand; } //========================================================================== // // // //========================================================================== FxMinusSign::~FxMinusSign() { SAFE_DELETE(Operand); } //========================================================================== // // // //========================================================================== FxExpression *FxMinusSign::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); SAFE_RESOLVE(Operand, ctx); if (Operand->ValueType.isNumeric()) { if (Operand->isConstant()) { ExpVal val = static_cast(Operand)->GetValue(); FxExpression *e = val.Type == VAL_Int? new FxConstant(-val.Int, ScriptPosition) : new FxConstant(-val.Float, ScriptPosition); delete this; return e; } ValueType = Operand->ValueType; return this; } else { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } } //========================================================================== // // // //========================================================================== 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_FLOP, from.RegNum, from.RegNum, FLOP_NEG); } return from; } //========================================================================== // // // //========================================================================== FxUnaryNotBitwise::FxUnaryNotBitwise(FxExpression *operand) : FxExpression(operand->ScriptPosition) { Operand=operand; } //========================================================================== // // // //========================================================================== FxUnaryNotBitwise::~FxUnaryNotBitwise() { SAFE_DELETE(Operand); } //========================================================================== // // // //========================================================================== FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); SAFE_RESOLVE(Operand, ctx); if (Operand->ValueType == VAL_Float /* lax */) { // DECORATE allows floats here so cast them to int. Operand = new FxIntCast(Operand); Operand = Operand->Resolve(ctx); if (Operand == NULL) { delete this; return NULL; } } if (Operand->ValueType != VAL_Int) { ScriptPosition.Message(MSG_ERROR, "Integer type expected"); delete this; return NULL; } if (Operand->isConstant()) { int result = ~static_cast(Operand)->GetValue().GetInt(); FxExpression *e = new FxConstant(result, ScriptPosition); delete this; return e; } ValueType = VAL_Int; return this; } //========================================================================== // // // //========================================================================== 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; } //========================================================================== // // // //========================================================================== FxUnaryNotBoolean::FxUnaryNotBoolean(FxExpression *operand) : FxExpression(operand->ScriptPosition) { Operand=operand; } //========================================================================== // // // //========================================================================== FxUnaryNotBoolean::~FxUnaryNotBoolean() { SAFE_DELETE(Operand); } //========================================================================== // // // //========================================================================== FxExpression *FxUnaryNotBoolean::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (Operand) { Operand = Operand->ResolveAsBoolean(ctx); } if (!Operand) { delete this; return NULL; } if (Operand->ValueType.isNumeric() || Operand->ValueType.isPointer()) { if (Operand->isConstant()) { bool result = !static_cast(Operand)->GetValue().GetBool(); FxExpression *e = new FxConstant(result, ScriptPosition); delete this; return e; } } else { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } ValueType = VAL_Int; return this; } //========================================================================== // // // //========================================================================== ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build) { ExpEmit from = Operand->Emit(build); assert(!from.Konst); ExpEmit to(build, REGT_INT); from.Free(build); // 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; } //========================================================================== // // // //========================================================================== FxBinary::FxBinary(int o, FxExpression *l, FxExpression *r) : FxExpression(l->ScriptPosition) { Operator=o; left=l; right=r; } //========================================================================== // // // //========================================================================== FxBinary::~FxBinary() { SAFE_DELETE(left); SAFE_DELETE(right); } //========================================================================== // // // //========================================================================== bool FxBinary::ResolveLR(FCompileContext& ctx, bool castnumeric) { RESOLVE(left, ctx); RESOLVE(right, ctx); if (!left || !right) { delete this; return false; } if (left->ValueType == VAL_Int && right->ValueType == VAL_Int) { ValueType = VAL_Int; } else if (left->ValueType.isNumeric() && right->ValueType.isNumeric()) { ValueType = VAL_Float; } else if (left->ValueType == VAL_Object && right->ValueType == VAL_Object) { ValueType = VAL_Object; } else if (left->ValueType == VAL_Class && right->ValueType == VAL_Class) { ValueType = VAL_Class; } else { ValueType = VAL_Unknown; } if (castnumeric) { // later! } return true; } void FxBinary::Promote(FCompileContext &ctx) { if (left->ValueType == VAL_Float && right->ValueType == VAL_Int) { right = (new FxFloatCast(right))->Resolve(ctx); } else if (left->ValueType == VAL_Int && right->ValueType == VAL_Float) { left = (new FxFloatCast(left))->Resolve(ctx); } } //========================================================================== // // // //========================================================================== FxAddSub::FxAddSub(int o, FxExpression *l, FxExpression *r) : FxBinary(o, l, r) { } //========================================================================== // // // //========================================================================== FxExpression *FxAddSub::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (!ResolveLR(ctx, true)) return NULL; if (!ValueType.isNumeric()) { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } else if (left->isConstant() && right->isConstant()) { if (ValueType == VAL_Float) { double v; double v1 = static_cast(left)->GetValue().GetFloat(); double v2 = static_cast(right)->GetValue().GetFloat(); v = Operator == '+'? v1 + v2 : Operator == '-'? v1 - v2 : 0; FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } else { int v; int v1 = static_cast(left)->GetValue().GetInt(); int v2 = static_cast(right)->GetValue().GetInt(); v = Operator == '+'? v1 + v2 : Operator == '-'? v1 - v2 : 0; FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } } Promote(ctx); return this; } //========================================================================== // // // //========================================================================== 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) { swapvalues(op1, op2); } assert(!op1.Konst); op1.Free(build); op2.Free(build); 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); op1.Free(build); op2.Free(build); 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; } } } //========================================================================== // // // //========================================================================== FxMulDiv::FxMulDiv(int o, FxExpression *l, FxExpression *r) : FxBinary(o, l, r) { } //========================================================================== // // // //========================================================================== FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (!ResolveLR(ctx, true)) return NULL; if (!ValueType.isNumeric()) { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } else if (left->isConstant() && right->isConstant()) { if (ValueType == VAL_Float) { double v; double v1 = static_cast(left)->GetValue().GetFloat(); double v2 = static_cast(right)->GetValue().GetFloat(); if (Operator != '*' && v2 == 0) { ScriptPosition.Message(MSG_ERROR, "Division by 0"); delete this; return NULL; } v = Operator == '*'? v1 * v2 : Operator == '/'? v1 / v2 : Operator == '%'? fmod(v1, v2) : 0; FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } else { int v; int v1 = static_cast(left)->GetValue().GetInt(); int v2 = static_cast(right)->GetValue().GetInt(); if (Operator != '*' && v2 == 0) { ScriptPosition.Message(MSG_ERROR, "Division by 0"); delete this; return NULL; } v = Operator == '*'? v1 * v2 : Operator == '/'? v1 / v2 : Operator == '%'? v1 % v2 : 0; FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } } Promote(ctx); return this; } //========================================================================== // // // //========================================================================== 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) { swapvalues(op1, op2); } assert(!op1.Konst); op1.Free(build); op2.Free(build); 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 == '/'); op1.Free(build); op2.Free(build); 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; } } } //========================================================================== // // // //========================================================================== FxCompareRel::FxCompareRel(int o, FxExpression *l, FxExpression *r) : FxBinary(o, l, r) { } //========================================================================== // // // //========================================================================== FxExpression *FxCompareRel::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (!ResolveLR(ctx, true)) return NULL; if (!ValueType.isNumeric()) { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } else if (left->isConstant() && right->isConstant()) { int v; if (ValueType == VAL_Float) { double v1 = static_cast(left)->GetValue().GetFloat(); double v2 = static_cast(right)->GetValue().GetFloat(); v = Operator == '<'? v1 < v2 : Operator == '>'? v1 > v2 : Operator == TK_Geq? v1 >= v2 : Operator == TK_Leq? v1 <= v2 : 0; } else { int v1 = static_cast(left)->GetValue().GetInt(); int v2 = static_cast(right)->GetValue().GetInt(); v = Operator == '<'? v1 < v2 : Operator == '>'? v1 > v2 : Operator == TK_Geq? v1 >= v2 : Operator == TK_Leq? v1 <= v2 : 0; } FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } Promote(ctx); ValueType = VAL_Int; return this; } //========================================================================== // // // //========================================================================== 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_LEF_RR, 0 } // <= }; int instr, check, index; ExpEmit to(build, REGT_INT); 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 { op2.Free(build); } if (op1.Konst) { instr += 2; } else { op1.Free(build); } // 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; } //========================================================================== // // // //========================================================================== FxCompareEq::FxCompareEq(int o, FxExpression *l, FxExpression *r) : FxBinary(o, l, r) { } //========================================================================== // // // //========================================================================== FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (!ResolveLR(ctx, true)) return NULL; if (!left || !right) { delete this; return NULL; } if (!ValueType.isNumeric() && !ValueType.isPointer()) { if (left->ValueType.Type == right->ValueType.Type) { // compare other types? if (left->ValueType == VAL_Sound || left->ValueType == VAL_Color || left->ValueType == VAL_Name) { left->ValueType = right->ValueType = VAL_Int; goto cont; } } ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } cont: if (left->isConstant() && right->isConstant()) { int v; if (ValueType == VAL_Float) { double v1 = static_cast(left)->GetValue().GetFloat(); double v2 = static_cast(right)->GetValue().GetFloat(); v = Operator == TK_Eq? v1 == v2 : v1 != v2; } else { int v1 = static_cast(left)->GetValue().GetInt(); int v2 = static_cast(right)->GetValue().GetInt(); v = Operator == TK_Eq? v1 == v2 : v1 != v2; } FxExpression *e = new FxConstant(v, ScriptPosition); delete this; return e; } Promote(ctx); ValueType = VAL_Int; return this; } //========================================================================== // // // //========================================================================== 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) { swapvalues(op1, op2); } assert(!op1.Konst); ExpEmit to(build, REGT_INT); instr = op1.RegType == REGT_INT ? OP_EQ_R : op1.RegType == REGT_FLOAT ? OP_EQF_R : OP_EQA_R; op1.Free(build); if (!op2.Konst) { op2.Free(build); } else { instr += 1; } // 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; } //========================================================================== // // // //========================================================================== FxBinaryInt::FxBinaryInt(int o, FxExpression *l, FxExpression *r) : FxBinary(o, l, r) { ValueType = VAL_Int; } //========================================================================== // // // //========================================================================== FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (!ResolveLR(ctx, false)) return NULL; if (ValueType == VAL_Float /* lax */) { // For DECORATE which allows floats here. if (left->ValueType != VAL_Int) { left = new FxIntCast(left); left = left->Resolve(ctx); } if (right->ValueType != VAL_Int) { right = new FxIntCast(right); right = right->Resolve(ctx); } if (left == NULL || right == NULL) { delete this; return NULL; } ValueType = VAL_Int; } if (ValueType != VAL_Int) { ScriptPosition.Message(MSG_ERROR, "Integer type expected"); delete this; return NULL; } else if (left->isConstant() && right->isConstant()) { int v1 = static_cast(left)->GetValue().GetInt(); int v2 = static_cast(right)->GetValue().GetInt(); FxExpression *e = new FxConstant( Operator == TK_LShift? v1 << v2 : Operator == TK_RShift? v1 >> v2 : Operator == TK_URShift? int((unsigned int)(v1) >> v2) : Operator == '&'? v1 & v2 : Operator == '|'? v1 | v2 : Operator == '^'? v1 ^ v2 : 0, ScriptPosition); delete this; return e; } return this; } //========================================================================== // // // //========================================================================== 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 = static_cast(right)->GetValue().GetInt(); op2.Konst = true; } else { op2 = right->Emit(build); assert(!op2.Konst); op2.Free(build); rop = op2.RegNum; } } else { // The other operators only take a constant on the right-hand side. op2 = right->Emit(build); if (op1.Konst) { swapvalues(op1, op2); } assert(!op1.Konst); rop = op2.RegNum; op2.Free(build); } if (!op1.Konst) { op1.Free(build); 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; } //========================================================================== // // // //========================================================================== FxBinaryLogical::FxBinaryLogical(int o, FxExpression *l, FxExpression *r) : FxExpression(l->ScriptPosition) { Operator=o; left=l; right=r; ValueType = VAL_Int; } //========================================================================== // // // //========================================================================== FxBinaryLogical::~FxBinaryLogical() { SAFE_DELETE(left); SAFE_DELETE(right); } //========================================================================== // // // //========================================================================== FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (left) left = left->ResolveAsBoolean(ctx); if (right) right = right->ResolveAsBoolean(ctx); if (!left || !right) { delete this; return NULL; } int b_left=-1, b_right=-1; if (left->isConstant()) b_left = static_cast(left)->GetValue().GetBool(); if (right->isConstant()) b_right = static_cast(right)->GetValue().GetBool(); // Do some optimizations. This will throw out all sub-expressions that are not // needed to retrieve the final result. if (Operator == TK_AndAnd) { if (b_left==0 || b_right==0) { FxExpression *x = new FxConstant(0, ScriptPosition); delete this; return x; } else if (b_left==1 && b_right==1) { FxExpression *x = new FxConstant(1, ScriptPosition); delete this; return x; } else if (b_left==1) { FxExpression *x = right; right=NULL; delete this; return x; } else if (b_right==1) { FxExpression *x = left; left=NULL; delete this; return x; } } else if (Operator == TK_OrOr) { if (b_left==1 || b_right==1) { FxExpression *x = new FxConstant(1, ScriptPosition); delete this; return x; } if (b_left==0 && b_right==0) { FxExpression *x = new FxConstant(0, ScriptPosition); delete this; return x; } else if (b_left==0) { FxExpression *x = right; right=NULL; delete this; return x; } else if (b_right==0) { FxExpression *x = left; left=NULL; delete this; 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; } //========================================================================== // // // //========================================================================== 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); op1.Free(build); 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); op2.Free(build); 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, 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); op2.Free(build); 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, 0); build->Emit(OP_JMP, 1); size_t target = build->Emit(OP_LI, to.RegNum, 1); build->Backpatch(patchspot, target); return to; } } //========================================================================== // // // //========================================================================== FxConditional::FxConditional(FxExpression *c, FxExpression *t, FxExpression *f) : FxExpression(c->ScriptPosition) { condition = c; truex=t; falsex=f; } //========================================================================== // // // //========================================================================== FxConditional::~FxConditional() { SAFE_DELETE(condition); SAFE_DELETE(truex); SAFE_DELETE(falsex); } //========================================================================== // // // //========================================================================== FxExpression *FxConditional::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (condition) condition = condition->ResolveAsBoolean(ctx); RESOLVE(truex, ctx); RESOLVE(falsex, ctx); ABORT(condition && truex && falsex); if (truex->ValueType == VAL_Int && falsex->ValueType == VAL_Int) ValueType = VAL_Int; else if (truex->ValueType.isNumeric() && falsex->ValueType.isNumeric()) ValueType = VAL_Float; //else if (truex->ValueType != falsex->ValueType) if (condition->isConstant()) { ExpVal condval = static_cast(condition)->GetValue(); bool result = condval.GetBool(); FxExpression *e = result? truex:falsex; delete (result? falsex:truex); falsex = truex = NULL; delete this; 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; } //========================================================================== // // // //========================================================================== 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, static_cast(truex)->GetValue().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, static_cast(falsex)->GetValue().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. falseop.Free(build); 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; } //========================================================================== // // // //========================================================================== FxAbs::FxAbs(FxExpression *v) : FxExpression(v->ScriptPosition) { val = v; ValueType = v->ValueType; } //========================================================================== // // // //========================================================================== FxAbs::~FxAbs() { SAFE_DELETE(val); } //========================================================================== // // // //========================================================================== FxExpression *FxAbs::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(val, ctx); if (!val->ValueType.isNumeric()) { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } else if (val->isConstant()) { ExpVal value = static_cast(val)->GetValue(); switch (value.Type) { case VAL_Int: value.Int = abs(value.Int); break; case VAL_Float: value.Float = fabs(value.Float); break; default: // shouldn't happen delete this; return NULL; } FxExpression *x = new FxConstant(value, ScriptPosition); delete this; return x; } ValueType = val->ValueType; return this; } //========================================================================== // // // //========================================================================== 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; } //========================================================================== // // // //========================================================================== FxMinMax::FxMinMax(TArray &expr, FName type, const FScriptPosition &pos) : FxExpression(pos), Type(type) { assert(expr.Size() > 0); assert(type == NAME_Min || type == NAME_Max); ValueType = VAL_Unknown; choices.Resize(expr.Size()); for (unsigned i = 0; i < expr.Size(); ++i) { choices[i] = expr[i]; } } //========================================================================== // // // //========================================================================== FxExpression *FxMinMax::Resolve(FCompileContext &ctx) { unsigned int i; int intcount, floatcount; CHECKRESOLVED(); // Determine if float or int intcount = floatcount = 0; for (i = 0; i < choices.Size(); ++i) { RESOLVE(choices[i], ctx); ABORT(choices[i]); if (choices[i]->ValueType == VAL_Float) { floatcount++; } else if (choices[i]->ValueType == VAL_Int) { intcount++; } else { ScriptPosition.Message(MSG_ERROR, "Arguments must be of type int or float"); delete this; return NULL; } } if (floatcount != 0) { ValueType = VAL_Float; if (intcount != 0) { // There are some ints that need to be cast to floats for (i = 0; i < choices.Size(); ++i) { if (choices[i]->ValueType == VAL_Int) { choices[i] = new FxFloatCast(choices[i]); RESOLVE(choices[i], ctx); ABORT(choices[i]); } } } } else { ValueType = VAL_Int; } // If at least two arguments are constants, they can be solved now. // Look for first constant for (i = 0; i < choices.Size(); ++i) { if (choices[i]->isConstant()) { ExpVal best = static_cast(choices[i])->GetValue(); // Compare against remaining constants, which are removed. // The best value gets stored in this one. for (unsigned j = i + 1; j < choices.Size(); ) { if (!choices[j]->isConstant()) { j++; } else { ExpVal value = static_cast(choices[j])->GetValue(); assert(value.Type == ValueType.Type); if (Type == NAME_Min) { if (value.Type == VAL_Float) { if (value.Float < best.Float) { best.Float = value.Float; } } else { if (value.Int < best.Int) { best.Int = value.Int; } } } else { if (value.Type == VAL_Float) { if (value.Float > best.Float) { best.Float = value.Float; } } else { if (value.Int > best.Int) { best.Int = value.Int; } } } delete choices[j]; choices[j] = NULL; choices.Delete(j); } } FxExpression *x = new FxConstant(best, ScriptPosition); if (i == 0 && choices.Size() == 1) { // Every choice was constant delete this; return x; } delete choices[i]; choices[i] = x; break; } } return this; } //========================================================================== // // // //========================================================================== static void EmitLoad(VMFunctionBuilder *build, const ExpEmit resultreg, const ExpVal &value) { if (resultreg.RegType == REGT_FLOAT) { build->Emit(OP_LKF, resultreg.RegNum, build->GetConstantFloat(value.GetFloat())); } else { build->EmitLoadInt(resultreg.RegNum, value.GetInt()); } } ExpEmit FxMinMax::Emit(VMFunctionBuilder *build) { unsigned i; int opcode, opA; assert(choices.Size() > 0); assert(OP_LTF_RK == OP_LTF_RR+1); assert(OP_LT_RK == OP_LT_RR+1); assert(OP_LEF_RK == OP_LEF_RR+1); assert(OP_LE_RK == OP_LE_RR+1); if (Type == NAME_Min) { opcode = ValueType.Type == VAL_Float ? OP_LEF_RR : OP_LE_RR; opA = 1; } else { opcode = ValueType.Type == VAL_Float ? OP_LTF_RR : OP_LT_RR; opA = 0; } ExpEmit bestreg; // Get first value into a register. This will also be the result register. if (choices[0]->isConstant()) { bestreg = ExpEmit(build, ValueType.Type == VAL_Float ? REGT_FLOAT : REGT_INT); EmitLoad(build, bestreg, static_cast(choices[0])->GetValue()); } else { bestreg = choices[0]->Emit(build); } // Compare every choice. Better matches get copied to the bestreg. for (i = 1; i < choices.Size(); ++i) { ExpEmit checkreg = choices[i]->Emit(build); assert(checkreg.RegType == bestreg.RegType); build->Emit(opcode + checkreg.Konst, opA, bestreg.RegNum, checkreg.RegNum); build->Emit(OP_JMP, 1); if (checkreg.Konst) { build->Emit(bestreg.RegType == REGT_FLOAT ? OP_LKF : OP_LK, bestreg.RegNum, checkreg.RegNum); } else { build->Emit(bestreg.RegType == REGT_FLOAT ? OP_MOVEF : OP_MOVE, bestreg.RegNum, checkreg.RegNum, 0); checkreg.Free(build); } } return bestreg; } //========================================================================== // // // //========================================================================== FxRandom::FxRandom(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) : FxExpression(pos) { if (mi != NULL && ma != NULL) { min = new FxParameter(new FxIntCast(mi)); max = new FxParameter(new FxIntCast(ma)); } else min = max = NULL; rng = r; ValueType = VAL_Int; } //========================================================================== // // // //========================================================================== FxRandom::~FxRandom() { SAFE_DELETE(min); SAFE_DELETE(max); } //========================================================================== // // // //========================================================================== FxExpression *FxRandom::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); if (min && max) { RESOLVE(min, ctx); RESOLVE(max, ctx); ABORT(min && max); assert(min->ValueType == ValueType.Type); assert(max->ValueType == ValueType.Type); } return this; }; //========================================================================== // // // //========================================================================== 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) { swapvalues(max, min); } ret->SetInt((*rng)(max - min + 1) + min); } return 1; } ExpEmit FxRandom::Emit(VMFunctionBuilder *build) { // Call DecoRandom to generate a random number. VMFunction *callfunc; PSymbol *sym = FindDecorateBuiltinFunction(NAME_DecoRandom, DecoRandom); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); 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) { min->Emit(build); max->Emit(build); 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; } //========================================================================== // // // //========================================================================== FxRandomPick::FxRandomPick(FRandom *r, TArray &expr, bool floaty, const FScriptPosition &pos) : FxExpression(pos) { assert(expr.Size() > 0); choices.Resize(expr.Size()); for (unsigned int index = 0; index < expr.Size(); index++) { if (floaty) { choices[index] = new FxFloatCast(expr[index]); } else { choices[index] = new FxIntCast(expr[index]); } } rng = r; ValueType = floaty ? VAL_Float : VAL_Int; } //========================================================================== // // // //========================================================================== FxRandomPick::~FxRandomPick() { } //========================================================================== // // // //========================================================================== FxExpression *FxRandomPick::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); for (unsigned int index = 0; index < choices.Size(); index++) { RESOLVE(choices[index], ctx); ABORT(choices[index]); assert(choices[index]->ValueType == ValueType.Type); } return this; }; //========================================================================== // // FxPick :: Emit // // The expression: // a = pick[rng](i_0, i_1, i_2, ..., i_n) // [where i_x is a complete expression and not just a value] // is syntactic sugar for: // // switch(random[rng](0, n)) { // case 0: a = i_0; // case 1: a = i_1; // case 2: a = i_2; // ... // case n: a = i_n; // } // //========================================================================== ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) { unsigned i; assert(choices.Size() > 0); // Call DecoRandom to generate a random number. VMFunction *callfunc; PSymbol *sym = FindDecorateBuiltinFunction(NAME_DecoRandom, DecoRandom); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); build->EmitParamInt(0); build->EmitParamInt(choices.Size() - 1); build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); ExpEmit resultreg(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, resultreg.RegNum); build->Emit(OP_IJMP, resultreg.RegNum, 0); // Free the result register now. The simple code generation algorithm should // automatically pick it as the destination register for each case. resultreg.Free(build); // For floating point results, we need to get a new register, since we can't // reuse the integer one used to store the random result. if (ValueType == VAL_Float) { resultreg = ExpEmit(build, REGT_FLOAT); resultreg.Free(build); } // Allocate space for the jump table. size_t jumptable = build->Emit(OP_JMP, 0); for (i = 1; i < choices.Size(); ++i) { build->Emit(OP_JMP, 0); } // Emit each case TArray finishes(choices.Size() - 1); for (unsigned i = 0; i < choices.Size(); ++i) { build->BackpatchToHere(jumptable + i); if (choices[i]->isConstant()) { EmitLoad(build, resultreg, static_cast(choices[i])->GetValue()); } else { ExpEmit casereg = choices[i]->Emit(build); if (casereg.RegNum != resultreg.RegNum) { // The result of the case is in a different register from what // was expected. Copy it to the one we wanted. resultreg.Reuse(build); // This is really just for the assert in Reuse() build->Emit(ValueType == VAL_Int ? OP_MOVE : OP_MOVEF, resultreg.RegNum, casereg.RegNum, 0); resultreg.Free(build); } // Free this register so the remaining cases can use it. casereg.Free(build); } // All but the final case needs a jump to the end of the expression's code. if (i + 1 < choices.Size()) { size_t loc = build->Emit(OP_JMP, 0); finishes.Push(loc); } } // Backpatch each case (except the last, since it ends here) to jump to here. for (i = 0; i < choices.Size() - 1; ++i) { build->BackpatchToHere(finishes[i]); } // The result register needs to be in-use when we return. // It should have been freed earlier, so restore its in-use flag. resultreg.Reuse(build); return resultreg; } //========================================================================== // // // //========================================================================== FxFRandom::FxFRandom(FRandom *r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) : FxRandom(r, NULL, NULL, pos) { if (mi != NULL && ma != NULL) { min = new FxParameter(new FxFloatCast(mi)); max = new FxParameter(new FxFloatCast(ma)); } ValueType = VAL_Float; } //========================================================================== // // // //========================================================================== 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) { swapvalues(max, min); } ret->SetFloat(frandom * (max - min) + min); } else { ret->SetFloat(frandom); } return 1; } ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) { // Call the DecoFRandom function to generate a floating point random number.. VMFunction *callfunc; PSymbol *sym = FindDecorateBuiltinFunction(NAME_DecoFRandom, DecoFRandom); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); 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) { min->Emit(build); max->Emit(build); 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; } //========================================================================== // // // //========================================================================== FxRandom2::FxRandom2(FRandom *r, FxExpression *m, const FScriptPosition &pos) : FxExpression(pos) { rng = r; if (m) mask = new FxIntCast(m); else mask = new FxConstant(-1, pos); mask = new FxParameter(mask); ValueType = VAL_Int; } //========================================================================== // // // //========================================================================== FxRandom2::~FxRandom2() { SAFE_DELETE(mask); } //========================================================================== // // // //========================================================================== FxExpression *FxRandom2::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(mask, ctx); return this; } //========================================================================== // // // //========================================================================== ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) { // Call the DecoRandom function to generate the random number. VMFunction *callfunc; PSymbol *sym = FindDecorateBuiltinFunction(NAME_DecoRandom, DecoRandom); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); mask->Emit(build); 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; } //========================================================================== // // // //========================================================================== FxIdentifier::FxIdentifier(FName name, const FScriptPosition &pos) : FxExpression(pos) { Identifier = name; } //========================================================================== // // // //========================================================================== FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) { PSymbol * sym; FxExpression *newex = NULL; //FBaseCVar * cv = NULL; //FString s; int num; //const PClass *Class; CHECKRESOLVED(); // see if the current class (if valid) defines something with this name. if ((sym = ctx.FindInClass(Identifier)) != NULL) { if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) { ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as class constant\n", Identifier.GetChars()); newex = FxConstant::MakeConstant(sym, ScriptPosition); } else if (sym->IsKindOf(RUNTIME_CLASS(PField))) { PField *vsym = static_cast(sym); ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as member variable, index %d\n", Identifier.GetChars(), vsym->Offset); newex = new FxClassMember((new FxSelf(ScriptPosition))->Resolve(ctx), vsym, ScriptPosition); } else { ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars()); } } // the damage property needs special handling else if (Identifier == NAME_Damage) { newex = new FxDamage(ScriptPosition); } // now check the global identifiers. else if ((sym = ctx.FindGlobal(Identifier)) != NULL) { if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) { ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as global constant\n", Identifier.GetChars()); newex = FxConstant::MakeConstant(sym, ScriptPosition); } else { ScriptPosition.Message(MSG_ERROR, "Invalid global identifier '%s'\n", Identifier.GetChars()); } } /* else if ((Class = PClass::FindClass(Identifier))) { pos.Message(MSG_DEBUGLOG, "Resolving name '%s' as class name\n", Identifier.GetChars()); newex = new FxClassType(Class, ScriptPosition); } } */ // also check for CVars /* else if ((cv = FindCVar(Identifier, NULL)) != NULL) { CLOG(CL_RESOLVE, LPrintf("Resolving name '%s' as cvar\n", Identifier.GetChars())); newex = new FxCVar(cv, ScriptPosition); } */ // 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); newex = new FxConstant(num, ScriptPosition); } else { ScriptPosition.Message(MSG_ERROR, "Unknown identifier '%s'", Identifier.GetChars()); newex = new FxConstant(0, ScriptPosition); } delete this; return newex? newex->Resolve(ctx) : NULL; } //========================================================================== // // // //========================================================================== FxSelf::FxSelf(const FScriptPosition &pos) : FxExpression(pos) { } //========================================================================== // // // //========================================================================== FxExpression *FxSelf::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (!ctx.cls) { // can't really happen with DECORATE's expression evaluator. ScriptPosition.Message(MSG_ERROR, "self used outside of a member function"); delete this; return NULL; } ValueType = ctx.cls; ValueType.Type = VAL_Object; return this; } //========================================================================== // // // //========================================================================== ExpEmit FxSelf::Emit(VMFunctionBuilder *build) { // self is always the first pointer passed to the function ExpEmit me(0, REGT_POINTER); me.Fixed = true; return me; } //========================================================================== // // // //========================================================================== FxDamage::FxDamage(const FScriptPosition &pos) : FxExpression(pos) { } //========================================================================== // // FxDamage :: Resolve // //========================================================================== FxExpression *FxDamage::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); ValueType = VAL_Int; return this; } //========================================================================== // // FxDamage :: Emit // // Call this actor's damage function, if it has one // //========================================================================== ExpEmit FxDamage::Emit(VMFunctionBuilder *build) { ExpEmit dmgval(build, REGT_INT); // Get damage function ExpEmit dmgfunc(build, REGT_POINTER); build->Emit(OP_LO, dmgfunc.RegNum, 0/*self*/, build->GetConstantInt(myoffsetof(AActor, Damage))); // If it's non-null... build->Emit(OP_EQA_K, 1, dmgfunc.RegNum, build->GetConstantAddress(0, ATAG_GENERIC)); size_t nulljump = build->Emit(OP_JMP, 0); // ...call it build->Emit(OP_PARAM, 0, REGT_POINTER, 0/*self*/); build->Emit(OP_CALL, dmgfunc.RegNum, 1, 1); build->Emit(OP_RESULT, 0, REGT_INT, dmgval.RegNum); size_t notnulljump = build->Emit(OP_JMP, 0); // Otherwise, use 0 build->BackpatchToHere(nulljump); build->EmitLoadInt(dmgval.RegNum, 0); build->BackpatchToHere(notnulljump); return dmgval; } //========================================================================== // // // //========================================================================== FxClassMember::FxClassMember(FxExpression *x, PField* mem, const FScriptPosition &pos) : FxExpression(pos) { classx = x; membervar = mem; AddressRequested = false; //if (classx->IsDefaultObject()) Readonly=true; } //========================================================================== // // // //========================================================================== FxClassMember::~FxClassMember() { SAFE_DELETE(classx); } //========================================================================== // // // //========================================================================== void FxClassMember::RequestAddress() { AddressRequested = true; } //========================================================================== // // // //========================================================================== FxExpression *FxClassMember::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(classx, ctx); if (classx->ValueType != VAL_Object && classx->ValueType != VAL_Class) { ScriptPosition.Message(MSG_ERROR, "Member variable requires a class or object"); delete this; return NULL; } PType *type = membervar->Type; PArray *arraytype = dyn_cast(type); if (arraytype != NULL) { type = arraytype->ElementType; } if (type->IsKindOf(RUNTIME_CLASS(PPointer))) { ValueType = VAL_Object; } else if (type->IsKindOf(RUNTIME_CLASS(PInt))) { ValueType = VAL_Int; } else if (type->IsKindOf(RUNTIME_CLASS(PFloat))) { ValueType = VAL_Float; } else { ScriptPosition.Message(MSG_ERROR, "Invalid type for member variable %s", membervar->SymbolName.GetChars()); delete this; return NULL; } if (arraytype != NULL) { ValueType.MakeArray(arraytype->ElementCount); } return this; } ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) { ExpEmit obj = classx->Emit(build); assert(obj.RegType == REGT_POINTER); if (AddressRequested) { if (membervar->Offset == 0) { return obj; } obj.Free(build); 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; } loc = ExpEmit(build, membervar->Type->GetRegType()); build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, obj.RegNum, offsetreg); obj.Free(build); return loc; } //========================================================================== // // // //========================================================================== FxArrayElement::FxArrayElement(FxExpression *base, FxExpression *_index) :FxExpression(base->ScriptPosition) { Array=base; index = _index; //AddressRequested = false; } //========================================================================== // // // //========================================================================== FxArrayElement::~FxArrayElement() { SAFE_DELETE(Array); SAFE_DELETE(index); } //========================================================================== // // // //========================================================================== /* void FxArrayElement::RequestAddress() { AddressRequested = true; } */ //========================================================================== // // // //========================================================================== FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(Array,ctx); SAFE_RESOLVE(index,ctx); if (index->ValueType == VAL_Float /* lax */) { // DECORATE allows floats here so cast them to int. index = new FxIntCast(index); index = index->Resolve(ctx); if (index == NULL) { delete this; return NULL; } } if (index->ValueType != VAL_Int) { ScriptPosition.Message(MSG_ERROR, "Array index must be integer"); delete this; return NULL; } if (Array->ValueType != VAL_Array) { ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays."); delete this; return NULL; } ValueType = Array->ValueType.GetBaseType(); if (ValueType != VAL_Int) { // int arrays only for now ScriptPosition.Message(MSG_ERROR, "Only integer arrays are supported."); delete this; return NULL; } Array->RequestAddress(); return this; } //========================================================================== // // in its current state this won't be able to do more than handle the args array. // //========================================================================== 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 = static_cast(index)->GetValue().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); indexv.Free(build); } start.Free(build); return dest; } //========================================================================== // // // //========================================================================== FxFunctionCall::FxFunctionCall(FxExpression *self, FName methodname, FArgumentList *args, const FScriptPosition &pos) : FxExpression(pos) { Self = self; MethodName = methodname; ArgList = args; } //========================================================================== // // // //========================================================================== FxFunctionCall::~FxFunctionCall() { SAFE_DELETE(Self); SAFE_DELETE(ArgList); } //========================================================================== // // // //========================================================================== FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) { for (size_t i = 0; i < countof(FxFlops); ++i) { if (MethodName == FxFlops[i].Name) { if (Self != NULL) { ScriptPosition.Message(MSG_ERROR, "Global functions cannot have a self pointer"); delete this; return NULL; } FxExpression *x = new FxFlopFunctionCall(i, ArgList, ScriptPosition); ArgList = NULL; delete this; return x->Resolve(ctx); } } int min, max, special; if (MethodName == NAME_ACS_NamedExecuteWithResult || MethodName == NAME_CallACS) { special = -ACS_ExecuteWithResult; min = 1; max = 5; } else { special = P_FindLineSpecial(MethodName.GetChars(), &min, &max); } if (special != 0 && min >= 0) { int paramcount = ArgList? ArgList->Size() : 0; if (paramcount < min) { ScriptPosition.Message(MSG_ERROR, "Not enough parameters for '%s' (expected %d, got %d)", MethodName.GetChars(), min, paramcount); delete this; return NULL; } else if (paramcount > max) { ScriptPosition.Message(MSG_ERROR, "too many parameters for '%s' (expected %d, got %d)", MethodName.GetChars(), max, paramcount); delete this; return NULL; } FxExpression *x = new FxActionSpecialCall(Self, special, ArgList, ScriptPosition); ArgList = NULL; delete this; return x->Resolve(ctx); } ScriptPosition.Message(MSG_ERROR, "Call to unknown function '%s'", MethodName.GetChars()); delete this; return NULL; } //========================================================================== // // FxActionSpecialCall // // If special is negative, then the first argument will be treated as a // name for ACS_NamedExecuteWithResult. // //========================================================================== FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgumentList *args, const FScriptPosition &pos) : FxExpression(pos) { Self = self; Special = special; ArgList = args; } //========================================================================== // // // //========================================================================== FxActionSpecialCall::~FxActionSpecialCall() { SAFE_DELETE(Self); SAFE_DELETE(ArgList); } //========================================================================== // // // //========================================================================== FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); bool failed = false; if (ArgList != NULL) { for(unsigned i = 0; i < ArgList->Size(); i++) { (*ArgList)[i] = (*ArgList)[i]->Resolve(ctx); if ((*ArgList)[i] == NULL) failed = true; if (Special < 0 && i == 0) { if ((*ArgList)[i]->ValueType != VAL_Name) { ScriptPosition.Message(MSG_ERROR, "Name expected for parameter %d", i); failed = true; } } else if ((*ArgList)[i]->ValueType != VAL_Int) { if ((*ArgList)[i]->ValueType == VAL_Float /* lax */) { (*ArgList)[i] = new FxIntCast((*ArgList)[i]); } else { ScriptPosition.Message(MSG_ERROR, "Integer expected for parameter %d", i); failed = true; } } } if (failed) { delete this; return NULL; } } ValueType = VAL_Int; return this; } //========================================================================== // // // //========================================================================== 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(P_ExecuteSpecial(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_PARAMI, abs(Special)); // pass special number build->Emit(OP_PARAM, 0, REGT_POINTER, 0); // pass self if (ArgList != NULL) { for (; i < ArgList->Size(); ++i) { FxExpression *argex = (*ArgList)[i]; if (Special < 0 && i == 0) { assert(argex->ValueType == VAL_Name); assert(argex->isConstant()); build->EmitParamInt(-static_cast(argex)->GetValue().GetName()); } else { assert(argex->ValueType == VAL_Int); if (argex->isConstant()) { build->EmitParamInt(static_cast(argex)->GetValue().GetInt()); } else { ExpEmit arg(argex->Emit(build)); build->Emit(OP_PARAM, 0, arg.RegType, arg.RegNum); arg.Free(build); } } } } // Call the DecoCallLineSpecial function to perform the desired special. VMFunction *callfunc; PSymbol *sym = FindDecorateBuiltinFunction(NAME_DecoCallLineSpecial, DecoCallLineSpecial); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); 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; } //========================================================================== // // FxVMFunctionCall // //========================================================================== FxVMFunctionCall::FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos) : FxExpression(pos) { Function = func; ArgList = args; } //========================================================================== // // // //========================================================================== FxVMFunctionCall::~FxVMFunctionCall() { SAFE_DELETE(ArgList); } //========================================================================== // // FxVMFunctionCall :: Resolve // //========================================================================== FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); bool failed = false; if (ArgList != NULL) { for (unsigned i = 0; i < ArgList->Size(); i++) { (*ArgList)[i] = (*ArgList)[i]->Resolve(ctx); if ((*ArgList)[i] == NULL) failed = true; } } if (failed) { delete this; return NULL; } TArray &rets = Function->Variants[0].Implementation->Proto->ReturnTypes; if (rets.Size() > 0) { // If more types are added to ParseNativeFunction(), add them here too. if (rets[0] == TypeSInt32) ValueType = VAL_Int; else if (rets[0] == TypeFloat64) ValueType = VAL_Float; else if (rets[0] == TypeAngle) ValueType = VAL_Angle; else if (rets[0] == TypeFixed) ValueType = VAL_Fixed; else if (rets[0] == TypeState) ValueType = VAL_State; else { ValueType = VAL_Int; assert(0 && "Unhandled return type in FxVMFunctionCall::Resolve"); } } return this; } //========================================================================== // // Assumption: This call is being made to generate code inside an action // method, so the first three address registers are all set up for such a // function. (self, stateowner, callingstate) // //========================================================================== ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { return Emit(build, false); } ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) { assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); int count = GetArgCount(); // Emit code to pass implied parameters if (Function->Flags & VARF_Method) { build->Emit(OP_PARAM, 0, REGT_POINTER, 0); count += 1; } if (Function->Flags & VARF_Action) { build->Emit(OP_PARAM, 0, REGT_POINTER, 1); build->Emit(OP_PARAM, 0, REGT_POINTER, 2); count += 2; } // Emit code to pass explicit parameters if (ArgList != NULL) { for (unsigned i = 0; i < ArgList->Size(); ++i) { (*ArgList)[i]->Emit(build); } } // Get a constant register for this function VMFunction *vmfunc = Function->Variants[0].Implementation; int funcaddr = build->GetConstantAddress(vmfunc, ATAG_OBJECT); // Emit the call if (tailcall) { // Tail call build->Emit(OP_TAIL_K, funcaddr, count, 0); return ExpEmit(); } else if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType()); build->Emit(OP_CALL_K, funcaddr, count, 1); build->Emit(OP_RESULT, 0, reg.RegType, reg.RegNum); return reg; } else { // Call, expecting no results build->Emit(OP_CALL_K, funcaddr, count, 0); return ExpEmit(); } } //========================================================================== // // // //========================================================================== FxFlopFunctionCall::FxFlopFunctionCall(size_t index, FArgumentList *args, const FScriptPosition &pos) : FxExpression(pos) { assert(index < countof(FxFlops) && "FLOP index out of range"); Index = index; ArgList = args; } //========================================================================== // // // //========================================================================== FxFlopFunctionCall::~FxFlopFunctionCall() { SAFE_DELETE(ArgList); } FxExpression *FxFlopFunctionCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (ArgList == NULL || ArgList->Size() != 1) { ScriptPosition.Message(MSG_ERROR, "%s only has one parameter", FName(FxFlops[Index].Name).GetChars()); delete this; return NULL; } (*ArgList)[0] = (*ArgList)[0]->Resolve(ctx); if ((*ArgList)[0] == NULL) { delete this; return NULL; } if (!(*ArgList)[0]->ValueType.isNumeric()) { ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter"); delete this; return NULL; } if ((*ArgList)[0]->isConstant()) { double v = static_cast((*ArgList)[0])->GetValue().GetFloat(); v = FxFlops[Index].Evaluate(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; } //========================================================================== // // //========================================================================== ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build) { ExpEmit v = (*ArgList)[0]->Emit(build); assert(!v.Konst && v.RegType == REGT_FLOAT); build->Emit(OP_FLOP, v.RegNum, v.RegNum, FxFlops[Index].Flop); return v; } //========================================================================== // // FxSequence :: Resolve // //========================================================================== FxExpression *FxSequence::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); for (unsigned i = 0; i < Expressions.Size(); ++i) { if (NULL == (Expressions[i] = Expressions[i]->Resolve(ctx))) { delete this; return NULL; } } return this; } //========================================================================== // // FxSequence :: Emit // //========================================================================== ExpEmit FxSequence::Emit(VMFunctionBuilder *build) { for (unsigned i = 0; i < Expressions.Size(); ++i) { ExpEmit v = Expressions[i]->Emit(build); // Throw away any result. We don't care about it. v.Free(build); } return ExpEmit(); } //========================================================================== // // FxSequence :: GetDirectFunction // //========================================================================== VMFunction *FxSequence::GetDirectFunction() { if (Expressions.Size() == 1) { return Expressions[0]->GetDirectFunction(); } return NULL; } //========================================================================== // // FxIfStatement // //========================================================================== FxIfStatement::FxIfStatement(FxExpression *cond, FxExpression *true_part, FxExpression *false_part, const FScriptPosition &pos) : FxExpression(pos) { Condition = cond; WhenTrue = true_part; WhenFalse = false_part; assert(cond != NULL); } FxIfStatement::~FxIfStatement() { SAFE_DELETE(Condition); SAFE_DELETE(WhenTrue); SAFE_DELETE(WhenFalse); } FxExpression *FxIfStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); if (WhenTrue == NULL && WhenFalse == NULL) { // We don't do anything either way, so disappear delete this; return NULL; } Condition = Condition->ResolveAsBoolean(ctx); ABORT(Condition); if (WhenTrue != NULL) { WhenTrue = WhenTrue->Resolve(ctx); ABORT(WhenTrue); } if (WhenFalse != NULL) { WhenFalse = WhenFalse->Resolve(ctx); ABORT(WhenFalse); } ValueType = VAL_Unknown; if (Condition->isConstant()) { ExpVal condval = static_cast(Condition)->GetValue(); bool result = condval.GetBool(); FxExpression *e = result ? WhenTrue : WhenFalse; delete (result ? WhenFalse : WhenTrue); WhenTrue = WhenFalse = NULL; delete this; return e; } return this; } ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build) { ExpEmit v; size_t jumpspot; FxExpression *path1, *path2; int condcheck; // This is pretty much copied from FxConditional, except we don't // keep any results. ExpEmit cond = Condition->Emit(build); assert(cond.RegType == REGT_INT && !cond.Konst); if (WhenTrue != NULL) { path1 = WhenTrue; path2 = WhenFalse; condcheck = 1; } else { // When there is only a false path, reverse the condition so we can // treat it as a true path. assert(WhenFalse != NULL); path1 = WhenFalse; path2 = NULL; condcheck = 0; } // Test condition. build->Emit(OP_EQ_K, condcheck, cond.RegNum, build->GetConstantInt(0)); jumpspot = build->Emit(OP_JMP, 0); cond.Free(build); // Evaluate first path v = path1->Emit(build); v.Free(build); if (path2 != NULL) { size_t path1jump = build->Emit(OP_JMP, 0); // Evaluate second path build->BackpatchToHere(jumpspot); v = path2->Emit(build); v.Free(build); jumpspot = path1jump; } build->BackpatchToHere(jumpspot); return ExpEmit(); } //========================================================================== // //========================================================================== FxReturnStatement::FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos) : FxExpression(pos), Call(call) { } FxReturnStatement::~FxReturnStatement() { SAFE_DELETE(Call); } FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); if (Call != NULL) { Call = static_cast(Call->Resolve(ctx)); ABORT(Call); } return this; } ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) { // If we return nothing, use a regular RET opcode. If we return // something, use TAIL to call the function. Our return type // should be compatible with the called function's return type. if (Call == NULL) { build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); } else { Call->Emit(build, true); } return ExpEmit(); } VMFunction *FxReturnStatement::GetDirectFunction() { // If this return statement calls a function with no arguments, // then it can be a "direct" function. That is, the DECORATE // definition can call that function directly without wrapping // it inside VM code. if (Call != NULL && Call->GetArgCount() == 0) { return Call->GetVMFunction(); } return NULL; } //========================================================================== // //========================================================================== FxClassTypeCast::FxClassTypeCast(PClass *dtype, FxExpression *x) : FxExpression(x->ScriptPosition) { desttype = dtype; basex=x; } //========================================================================== // // // //========================================================================== FxClassTypeCast::~FxClassTypeCast() { SAFE_DELETE(basex); } //========================================================================== // // // //========================================================================== FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(basex, ctx); if (basex->ValueType != VAL_Name) { ScriptPosition.Message(MSG_ERROR, "Cannot convert to class type"); delete this; return NULL; } if (basex->isConstant()) { FName clsname = static_cast(basex)->GetValue().GetName(); PClass *cls = NULL; if (clsname != NAME_None) { cls = PClass::FindClass(clsname); if (cls == NULL) { /* lax */ // Since this happens in released WADs it must pass without a terminal error... :( ScriptPosition.Message(MSG_WARNING, "Unknown class name '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); } else { if (!cls->IsDescendantOf(desttype)) { ScriptPosition.Message(MSG_ERROR, "class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); delete this; return NULL; } ScriptPosition.Message(MSG_DEBUG, "resolving '%s' as class name", clsname.GetChars()); } } FxExpression *x = new FxConstant(cls, ScriptPosition); delete this; return x; } return this; } //========================================================================== // // // //========================================================================== 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), ATAG_OBJECT); 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)); // Call the DecoNameToClass function to convert from 'name' to class. VMFunction *callfunc; PSymbol *sym = FindDecorateBuiltinFunction(NAME_DecoNameToClass, DecoNameToClass); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); 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); clsname.Free(build); return dest; } //========================================================================== // // // //========================================================================== FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); if (ctx.cls->NumOwnedStates == 0) { // This can't really happen assert(false); } if (ctx.cls->NumOwnedStates <= index) { ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d", ctx.cls->TypeName.GetChars(), index); delete this; return NULL; } FxExpression *x = new FxConstant(ctx.cls->OwnedStates + index, ScriptPosition); delete this; return x; } //========================================================================== // // // //========================================================================== FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos) :FxExpression(pos) { FName scopename; FString statestring = _statestring; int scopeindex = statestring.IndexOf("::"); if (scopeindex >= 0) { scopename = FName(statestring, scopeindex, false); statestring = statestring.Right(statestring.Len() - scopeindex - 2); } else { scopename = NULL; } names = MakeStateNameList(statestring); names.Insert(0, scopename); scope = NULL; } //========================================================================== // // // //========================================================================== FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); if (names[0] == NAME_None) { scope = NULL; } else if (names[0] == NAME_Super) { scope = dyn_cast(ctx.cls->ParentClass); } else { scope = PClass::FindActor(names[0]); if (scope == NULL) { ScriptPosition.Message(MSG_ERROR, "Unknown class '%s' in state label", names[0].GetChars()); delete this; return NULL; } else if (!scope->IsDescendantOf(ctx.cls)) { ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(),ctx.cls->TypeName.GetChars()); delete this; return NULL; } } if (scope != NULL) { FState *destination = NULL; // If the label is class specific we can resolve it right here if (names[1] != NAME_None) { destination = scope->FindState(names.Size()-1, &names[1], false); if (destination == NULL) { ScriptPosition.Message(MSG_WARNING, "Unknown state jump destination"); /* lax */ return this; } } FxExpression *x = new FxConstant(destination, ScriptPosition); delete this; return x; } names.Delete(0); names.ShrinkToFit(); ValueType = VAL_State; return this; } //========================================================================== // // // //========================================================================== static int DoFindState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, FName *names, int numnames) { PARAM_OBJECT_AT(0, self, AActor); FState *state = self->GetClass()->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, ATAG_STATE); return 1; } // Find a state with any number of dots in its name. int DecoFindMultiNameState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) { assert(numparam > 1); assert(numret == 1); assert(ret->RegType == REGT_POINTER); FName *names = (FName *)alloca((numparam - 1) * sizeof(FName)); for (int i = 1; i < numparam; ++i) { PARAM_NAME_AT(i, zaname); names[i - 1] = zaname; } return DoFindState(stack, param, numparam, ret, names, numparam - 1); } // Find a state without any dots in its name. int DecoFindSingleNameState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) { assert(numparam == 2); assert(numret == 1); assert(ret->RegType == REGT_POINTER); PARAM_NAME_AT(1, zaname); return DoFindState(stack, param, numparam, ret, &zaname, 1); } ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build) { ExpEmit dest(build, REGT_POINTER); build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // pass stateowner for (unsigned i = 0; i < names.Size(); ++i) { build->EmitParamInt(names[i]); } // For one name, use the DecoFindSingleNameState function. For more than // one name, use the DecoFindMultiNameState function. VMFunction *callfunc; PSymbol *sym; if (names.Size() == 1) { sym = FindDecorateBuiltinFunction(NAME_DecoFindSingleNameState, DecoFindSingleNameState); } else { sym = FindDecorateBuiltinFunction(NAME_DecoFindMultiNameState, DecoFindMultiNameState); } assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); 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; } //========================================================================== // // // //========================================================================== FxDamageValue::FxDamageValue(FxExpression *v, bool calc) : FxExpression(v->ScriptPosition) { val = v; ValueType = VAL_Unknown; Calculated = calc; MyFunction = NULL; if (!calc) { assert(v->isConstant() && "Non-calculated damage must be constant"); } } FxDamageValue::~FxDamageValue() { SAFE_DELETE(val); } FxExpression *FxDamageValue::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(val, ctx) if (!val->ValueType.isNumeric()) { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); delete this; return NULL; } return this; } // This is a highly-specialized "expression" type that emits a complete function. ExpEmit FxDamageValue::Emit(VMFunctionBuilder *build) { if (val->isConstant()) { build->EmitRetInt(0, false, static_cast(val)->GetValue().Int); } else { ExpEmit emitval = val->Emit(build); assert(emitval.RegType == REGT_INT); build->Emit(OP_RET, 0, REGT_INT | (emitval.Konst ? REGT_KONST : 0), emitval.RegNum); } build->Emit(OP_RETI, 1 | RET_FINAL, Calculated); return ExpEmit(); }