Changed opcode implementation to native function implementation

This commit is contained in:
ZZYZX 2017-01-13 23:49:36 +02:00 committed by Christoph Oelckers
parent ee2ecf1450
commit a8beb51ca3
5 changed files with 204 additions and 176 deletions

View file

@ -700,6 +700,7 @@ xx(BuiltinFindSingleNameState)
xx(BuiltinHandleRuntimeState) xx(BuiltinHandleRuntimeState)
xx(BuiltinGetDefault) xx(BuiltinGetDefault)
xx(BuiltinClassCast) xx(BuiltinClassCast)
xx(BuiltinFormat)
xx(Damage) xx(Damage)
// basic type names // basic type names

View file

@ -8358,6 +8358,7 @@ ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build)
FxFormat::FxFormat(FArgumentList &args, const FScriptPosition &pos) FxFormat::FxFormat(FArgumentList &args, const FScriptPosition &pos)
: FxExpression(EFX_Format, pos) : FxExpression(EFX_Format, pos)
{ {
EmitTail = false;
ArgList = std::move(args); ArgList = std::move(args);
} }
@ -8371,6 +8372,24 @@ FxFormat::~FxFormat()
{ {
} }
//==========================================================================
//
//
//
//==========================================================================
PPrototype *FxFormat::ReturnProto()
{
EmitTail = true;
return FxExpression::ReturnProto();
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxFormat::Resolve(FCompileContext& ctx) FxExpression *FxFormat::Resolve(FCompileContext& ctx)
{ {
CHECKRESOLVED(); CHECKRESOLVED();
@ -8415,16 +8434,191 @@ FxExpression *FxFormat::Resolve(FCompileContext& ctx)
// //
//========================================================================== //==========================================================================
ExpEmit FxFormat::Emit(VMFunctionBuilder *build) static int BuiltinFormat(VMValue *args, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{ {
ExpEmit to = ExpEmit(build, REGT_STRING); assert(args[0].Type == REGT_STRING);
for (int i = 0; i < ArgList.Size(); i++) FString fmtstring = args[0].s().GetChars();
// note: we don't need a real printf format parser.
// enough to simply find the subtitution tokens and feed them to the real printf after checking types.
// https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification
FString output;
bool in_fmt = false;
FString fmt_current;
int argnum = 1;
int argauto = 1;
// % = starts
// [0-9], -, +, \s, 0, #, . continue
// %, s, d, i, u, fF, eE, gG, xX, o, c, p, aA terminate
// various type flags are not supported. not like stuff like 'hh' modifier is to be used in the VM.
// the only combination that is parsed locally is %n$...
bool haveargnums = false;
for (int i = 0; i < fmtstring.Len(); i++)
{ {
EmitParameter(build, ArgList[i], ScriptPosition); char c = fmtstring[i];
if (in_fmt)
{
if ((c >= '0' && c <= '9') ||
c == '-' || c == '+' || (c == ' ' && fmt_current[fmt_current.Len() - 1] != ' ') || c == '#' || c == '.')
{
fmt_current += c;
}
else if (c == '$') // %number$format
{
if (!haveargnums && argauto > 1)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
FString argnumstr = fmt_current.Mid(1);
if (!argnumstr.IsInt()) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for argument number, got '%s'.", argnumstr.GetChars());
argnum = argnumstr.ToLong();
if (argnum < 1 || argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format (tried to access argument %d, %d total).", argnum, numparam);
fmt_current = "%";
haveargnums = true;
}
else
{
fmt_current += c;
switch (c)
{
// string
case 's':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not a string
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].s().GetChars());
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
// pointer
case 'p':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not a string
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].a);
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
// int formats (including char)
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
case 'c':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not an int
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_INT &&
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToInt());
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
// double formats
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not a float
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_INT &&
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToDouble());
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
default:
// invalid character
output += fmt_current;
in_fmt = false;
break;
}
}
}
else
{
if (c == '%')
{
if (i + 1 < fmtstring.Len() && fmtstring[i + 1] == '%')
{
output += '%';
i++;
}
else
{
in_fmt = true;
fmt_current = "%";
}
}
else
{
output += c;
}
}
} }
build->Emit(OP_STRFMT, to.RegNum, ArgList.Size(), 0); ACTION_RETURN_STRING(output);
return to; }
ExpEmit FxFormat::Emit(VMFunctionBuilder *build)
{
// Call DecoRandom to generate a random number.
VMFunction *callfunc;
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinFormat, BuiltinFormat);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
for (int i = 0; i < ArgList.Size(); i++)
EmitParameter(build, ArgList[i], ScriptPosition);
build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), ArgList.Size(), 1);
if (EmitTail)
{
ExpEmit call;
call.Final = true;
return call;
}
ExpEmit out(build, REGT_STRING);
build->Emit(OP_RESULT, 0, REGT_STRING, out.RegNum);
return out;
} }

View file

@ -1533,19 +1533,21 @@ public:
//========================================================================== //==========================================================================
// //
// FxFormatFunctionCall // FxFormat
// //
//========================================================================== //==========================================================================
class FxFormat : public FxExpression class FxFormat : public FxExpression
{ {
FArgumentList ArgList; FArgumentList ArgList;
bool EmitTail;
public: public:
FxFormat(FArgumentList &args, const FScriptPosition &pos); FxFormat(FArgumentList &args, const FScriptPosition &pos);
~FxFormat(); ~FxFormat();
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
PPrototype *ReturnProto();
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
}; };

View file

@ -862,174 +862,6 @@ begin:
} }
NEXTOP; NEXTOP;
OP(STRFMT) :
{
ASSERTS(a);
assert(B <= f->NumParam);
int countparam = B;
assert(countparam >= 1);
VMValue* args = reg.param + f->NumParam - B;
assert(args[0].Type == REGT_STRING);
FString fmtstring = args[0].s().GetChars();
// note: we don't need a real printf format parser.
// enough to simply find the subtitution tokens and feed them to the real printf after checking types.
// https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification
FString output;
bool in_fmt = false;
FString fmt_current;
int argnum = 1;
int argauto = 1;
// % = starts
// [0-9], -, +, \s, 0, #, . continue
// %, s, d, i, u, fF, eE, gG, xX, o, c, p, aA terminate
// various type flags are not supported. not like stuff like 'hh' modifier is to be used in the VM.
// the only combination that is parsed locally is %n$...
bool haveargnums = false;
for (int i = 0; i < fmtstring.Len(); i++)
{
char c = fmtstring[i];
if (in_fmt)
{
if ((c >= '0' && c <= '9') ||
c == '-' || c == '+' || (c == ' ' && fmt_current[fmt_current.Len() - 1] != ' ') || c == '#' || c == '.')
{
fmt_current += c;
}
else if (c == '$') // %number$format
{
if (!haveargnums && argauto > 1)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
FString argnumstr = fmt_current.Mid(1);
if (!argnumstr.IsInt()) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for argument number, got '%s'.", argnumstr.GetChars());
argnum = argnumstr.ToLong();
if (argnum < 1 || argnum >= countparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format (tried to access argument %d, %d total).", argnum, countparam);
fmt_current = "%";
haveargnums = true;
}
else
{
fmt_current += c;
switch (c)
{
// string
case 's':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not a string
if (argnum >= countparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].s().GetChars());
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
// pointer
case 'p':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not a string
if (argnum >= countparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].a);
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
// int formats (including char)
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
case 'c':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not an int
if (argnum >= countparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_INT &&
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToInt());
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
// double formats
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A':
{
if (argnum < 0 && haveargnums)
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
in_fmt = false;
// fail if something was found, but it's not a float
if (argnum >= countparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
if (args[argnum].Type != REGT_INT &&
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
// append
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToDouble());
if (!haveargnums) argnum = ++argauto;
else argnum = -1;
break;
}
default:
// invalid character
output += fmt_current;
in_fmt = false;
break;
}
}
}
else
{
if (c == '%')
{
if (i + 1 < fmtstring.Len() && fmtstring[i + 1] == '%')
{
output += '%';
i++;
}
else
{
in_fmt = true;
fmt_current = "%";
}
}
else
{
output += c;
}
}
}
// finalize parameters
for (b = B; b != 0; --b)
reg.param[--f->NumParam].~VMValue();
reg.s[a] = output;
}
NEXTOP;
OP(SLL_RR): OP(SLL_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C); ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] << reg.d[C]; reg.d[a] = reg.d[B] << reg.d[C];

View file

@ -120,7 +120,6 @@ xx(BOUND_R, bound, RIRI, NOP, 0, 0), // if rA >= rB, throw exception
xx(CONCAT, concat, RSRSRS, NOP, 0, 0), // sA = sB..sC xx(CONCAT, concat, RSRSRS, NOP, 0, 0), // sA = sB..sC
xx(LENS, lens, RIRS, NOP, 0, 0), // dA = sB.Length xx(LENS, lens, RIRS, NOP, 0, 0), // dA = sB.Length
xx(CMPS, cmps, I8RXRX, NOP, 0, 0), // if ((skB op skC) != (A & 1)) then pc++ xx(CMPS, cmps, I8RXRX, NOP, 0, 0), // if ((skB op skC) != (A & 1)) then pc++
xx(STRFMT, strfmt, RIRIRI, NOP, 0, 0),
// Integer math. // Integer math.
xx(SLL_RR, sll, RIRIRI, NOP, 0, 0), // dA = dkB << diC xx(SLL_RR, sll, RIRIRI, NOP, 0, 0), // dA = dkB << diC