/* * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Initial Developer of this code is David Baum. * Portions created by David Baum are Copyright (C) 1999 David Baum. * All Rights Reserved. * * Portions created by John Hansen are Copyright (C) 2005 John Hansen. * All Rights Reserved. * */ #include "Expr.h" #include "RCX_Cmd.h" #include "Bytecode.h" #include "Error.h" #include "RCX_Target.h" #define MAX_INT 65535 #define MIN_INT -32768 // suitable sources for math operations (if target has constrained math) #define MATH_MASK (TYPEMASK(kRCX_VariableType) + \ TYPEMASK(kRCX_ConstantType)) static bool IsTypeInMask(long mask, RCX_ValueType type); Expr::~Expr() { } RCX_Value Expr::EmitAny(Bytecode &b) const { int v; if (Evaluate(v)) { if (v < MIN_INT || v > MAX_INT) Error(kErr_NumberRange).Raise(&fLoc); return RCX_VALUE(kRCX_ConstantType, v); } else return EmitAny_(b); } bool Expr::EmitTo(Bytecode &b, int dst) const { int v; if (Evaluate(v)) { b.AddMove(dst, RCX_VALUE(kRCX_ConstantType, v)); return true; } else return EmitTo_(b, dst); } RCX_Value Expr::EmitConstrained(Bytecode &b, long mask, bool canUseLocals) const { /* * There's one case that still isn't optimized...if locals cannot be used, * but EmitAny() wants a temp (such as for a math operation), then it is * possible for a local temp to be allocated, which just forces a move to * a global temp later on. It would be better if EmitAny() could be told * not to use locals, but this could involve a lot of tweaking. A possible * hack is to put the VarAllocator into kGlobal mode prior to an AsmField * calling EmitConstrained, then restoring the mode later on. */ RCX_Value ea = EmitAny(b); if (ea == kIllegalEA) return ea; RCX_ValueType type = RCX_VALUE_TYPE(ea); // check ea was allowed if (IsTypeInMask(mask, type) && (canUseLocals || !b.IsLocalEA(ea))) { return ea; } // must move to another ea // if registers are not valid, then return illegal EA if (!IsTypeInMask(mask, kRCX_VariableType)) { Error(kErr_BadExpression).Raise(&GetLoc()); return kIllegalEA; } // move into temp register int dst = GetTempVar(b, canUseLocals); if (dst == kIllegalVar) return kIllegalEA; b.AddMove(dst, ea); // when using arrays, there's a special case when the ea is // temp but not a variable. Another case arises if the original // emit was to a temp local, but they aren't allowed. // call ReleaseTempEA just in case b.ReleaseTempEA(ea); ea = RCX_VALUE(kRCX_VariableType, dst); return ea; } bool IsTypeInMask(long mask, RCX_ValueType type) { // special case, when mask==0 all types are allowed if (!mask) return true; return (1L << type) & mask; } RCX_Value Expr::EmitMath(Bytecode &b) const { // emit expression to an ea that is suitable for math operations if (b.GetTarget()->fRestrictedMath) return EmitConstrained(b, MATH_MASK); else return EmitAny(b); } bool Expr::EmitSide(Bytecode &b) const { return EmitSide_(b); } bool Expr::EmitBranch(Bytecode &b, int label, bool condition) const { int v; if (Evaluate(v)) { if ((v && condition) || (!v && !condition)) { b.AddJump(label); } return true; } return EmitBranch_(b, label, condition); } RCX_Value Expr::GetStaticEA() const { int v; if (Evaluate(v)) { return RCX_VALUE(kRCX_ConstantType, v); } return GetStaticEA_(); } RCX_Value Expr::GetStaticEA_() const { return kIllegalEA; } bool Expr::EmitTo_(Bytecode &b, int dst) const { RCX_Value ea = EmitAny_(b); if (ea == kIllegalEA) return false; b.AddMove(dst, ea); b.ReleaseTempEA(ea); return true; } bool Expr::EmitSide_(Bytecode &b) const { vector v; GetExprs(v); for(int i=0; i<(int)v.size(); ++i) if (!v[i]->EmitSide(b)) return false; return true; } bool Expr::EmitBranch_(Bytecode &b, int label, bool condition) const { RCX_Value ea = EmitConstrained(b, TEST_MASK); if (ea==kIllegalEA) return false; // compare ea to 0 and branch b.AddTest( RCX_VALUE(kRCX_ConstantType,0), condition ? kRCX_NotEqualTo : kRCX_EqualTo, ea, label); b.ReleaseTempEA(ea); return true; } RCX_Value Expr::EmitBoolAny(Bytecode &b) const { int dst = GetTempVar(b); if (dst < 0) return kIllegalEA; RCX_Value dstEA = RCX_VALUE(kRCX_VariableType, dst); if (!EmitBoolTo(b, dst)) { b.ReleaseTempEA(dstEA); return kIllegalEA; } return dstEA; } bool Expr::EmitBoolTo(Bytecode &b, int dst) const { int doneLabel = b.NewLabel(); if (Contains(dst)) { /* * if condition then goto 1 * dst = 0 * goto done * 1: * dst = 1 * done: */ int setLabel = b.NewLabel(); EmitBranch(b, setLabel, true); b.AddMove(dst, RCX_VALUE(kRCX_ConstantType, 0)); b.AddJump(doneLabel); b.SetLabel(setLabel); b.AddMove(dst, RCX_VALUE(kRCX_ConstantType, 1)); } else { /* dst = 0 * if !condition then goto done * dst = 1 * done: */ b.AddMove(dst, RCX_VALUE(kRCX_ConstantType, 0)); EmitBranch(b, doneLabel, false); b.AddMove(dst, RCX_VALUE(kRCX_ConstantType, 1)); } b.SetLabel(doneLabel); return true; } int Expr::GetTempVar(Bytecode &b, bool canUseLocals) const { int var = b.GetTempVar(canUseLocals); if (var == kIllegalVar) { Error(kErr_NoMoreTemps).Raise(&GetLoc()); } return var; }