From 89b437bb4edd63f4ee6cca3f5dcfcd734b5c3209 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 28 Nov 2023 07:35:01 -0800 Subject: [PATCH] Add SUBRK and DIVRK bytecode instructions to bytecode v5 (#1115) Right now, we can compile R\*K for all arithmetic instructions, but K\*R gets compiled into two instructions (LOADN/LOADK + arithmetic opcode). This is problematic since it leads to reduced performance for some code. However, we'd like to avoid adding reverse variants of ADDK et al for all opcodes to avoid the increase in I$ footprint for interpreter. Looking at the arithmetic instructions, % and // don't have interesting use cases for K\*V; ^ is sometimes used with constant on the left hand side but this would need to call pow() by necessity in all cases so it would be slow regardless of the dispatch overhead. This leaves the four basic arithmetic operations. For + and \*, we can implement a compiler-side optimization in the future that transforms K\*R to R\*K automatically. This could either be done unconditionally at -O2, or conditionally based on the type of the value (driven by type annotations / inference) -- this technically changes behavior in presence of metamethods, although it might be sensible to just always do this because non-commutative +/* are evil. However, for - and / it is impossible for the compiler to optimize this in the future, so we need dedicated opcodes. This only increases the interpreter size by ~300 bytes (~1.5%) on X64. This makes spectral-norm and math-partial-sums 6% faster; maybe more importantly, voxelgen gets 1.5% faster (so this change does have real-world impact). To avoid the proliferation of bytecode versions this change piggybacks on the bytecode version bump that was just made in 604 for vector constants; we would still be able to enable these independently but we'll consider v5 complete when both are enabled. Related: #626 --------- Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com> --- CodeGen/include/Luau/IrVisitUseDef.h | 5 ++ CodeGen/src/EmitCommonX64.cpp | 4 +- CodeGen/src/EmitCommonX64.h | 2 +- CodeGen/src/IrBuilder.cpp | 6 +++ CodeGen/src/IrLoweringA64.cpp | 6 ++- CodeGen/src/IrLoweringX64.cpp | 9 ++-- CodeGen/src/IrTranslation.cpp | 41 ++++++++++---- CodeGen/src/IrTranslation.h | 1 + Common/include/Luau/Bytecode.h | 13 +++-- Compiler/src/BytecodeBuilder.cpp | 22 +++++++- Compiler/src/Compiler.cpp | 16 ++++++ VM/src/lvmexecute.cpp | 57 ++++++++++++++++---- tests/Compiler.test.cpp | 81 ++++++++++++++++++---------- 13 files changed, 203 insertions(+), 60 deletions(-) diff --git a/CodeGen/include/Luau/IrVisitUseDef.h b/CodeGen/include/Luau/IrVisitUseDef.h index 8134e0d5b..603f1ec59 100644 --- a/CodeGen/include/Luau/IrVisitUseDef.h +++ b/CodeGen/include/Luau/IrVisitUseDef.h @@ -41,6 +41,11 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i break; // A <- B, C case IrCmd::DO_ARITH: + visitor.maybeUse(inst.b); // Argument can also be a VmConst + visitor.maybeUse(inst.c); // Argument can also be a VmConst + + visitor.def(inst.a); + break; case IrCmd::GET_TABLE: visitor.use(inst.b); visitor.maybeUse(inst.c); // Argument can also be a VmConst diff --git a/CodeGen/src/EmitCommonX64.cpp b/CodeGen/src/EmitCommonX64.cpp index 008dadd57..014f5a46f 100644 --- a/CodeGen/src/EmitCommonX64.cpp +++ b/CodeGen/src/EmitCommonX64.cpp @@ -148,12 +148,12 @@ void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, Regi build.jcc(ConditionX64::NotZero, label); } -void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, TMS tm) +void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, OperandX64 b, OperandX64 c, TMS tm) { IrCallWrapperX64 callWrap(regs, build); callWrap.addArgument(SizeX64::qword, rState); callWrap.addArgument(SizeX64::qword, luauRegAddress(ra)); - callWrap.addArgument(SizeX64::qword, luauRegAddress(rb)); + callWrap.addArgument(SizeX64::qword, b); callWrap.addArgument(SizeX64::qword, c); callWrap.addArgument(SizeX64::dword, tm); callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarith)]); diff --git a/CodeGen/src/EmitCommonX64.h b/CodeGen/src/EmitCommonX64.h index 3418a09f2..bc1f99c90 100644 --- a/CodeGen/src/EmitCommonX64.h +++ b/CodeGen/src/EmitCommonX64.h @@ -200,7 +200,7 @@ ConditionX64 getConditionInt(IrCondition cond); void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, RegisterX64 table, int pcpos); void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, Label& label); -void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, TMS tm); +void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, OperandX64 b, OperandX64 c, TMS tm); void callLengthHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int rb); void callGetTable(IrRegAllocX64& regs, AssemblyBuilderX64& build, int rb, OperandX64 c, int ra); void callSetTable(IrRegAllocX64& regs, AssemblyBuilderX64& build, int rb, OperandX64 c, int ra); diff --git a/CodeGen/src/IrBuilder.cpp b/CodeGen/src/IrBuilder.cpp index 56bbf9040..765433a4f 100644 --- a/CodeGen/src/IrBuilder.cpp +++ b/CodeGen/src/IrBuilder.cpp @@ -381,6 +381,12 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i) case LOP_POWK: translateInstBinaryK(*this, pc, i, TM_POW); break; + case LOP_SUBRK: + translateInstBinaryRK(*this, pc, i, TM_SUB); + break; + case LOP_DIVRK: + translateInstBinaryRK(*this, pc, i, TM_DIV); + break; case LOP_NOT: translateInstNot(*this, pc); break; diff --git a/CodeGen/src/IrLoweringA64.cpp b/CodeGen/src/IrLoweringA64.cpp index 6a1733a53..5d0be75a0 100644 --- a/CodeGen/src/IrLoweringA64.cpp +++ b/CodeGen/src/IrLoweringA64.cpp @@ -1067,7 +1067,11 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) regs.spill(build, index); build.mov(x0, rState); build.add(x1, rBase, uint16_t(vmRegOp(inst.a) * sizeof(TValue))); - build.add(x2, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); + + if (inst.b.kind == IrOpKind::VmConst) + emitAddOffset(build, x2, rConstants, vmConstOp(inst.b) * sizeof(TValue)); + else + build.add(x2, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); if (inst.c.kind == IrOpKind::VmConst) emitAddOffset(build, x3, rConstants, vmConstOp(inst.c) * sizeof(TValue)); diff --git a/CodeGen/src/IrLoweringX64.cpp b/CodeGen/src/IrLoweringX64.cpp index 74a5bfd68..ca06ca158 100644 --- a/CodeGen/src/IrLoweringX64.cpp +++ b/CodeGen/src/IrLoweringX64.cpp @@ -962,11 +962,12 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) break; } case IrCmd::DO_ARITH: - if (inst.c.kind == IrOpKind::VmReg) - callArithHelper(regs, build, vmRegOp(inst.a), vmRegOp(inst.b), luauRegAddress(vmRegOp(inst.c)), TMS(intOp(inst.d))); - else - callArithHelper(regs, build, vmRegOp(inst.a), vmRegOp(inst.b), luauConstantAddress(vmConstOp(inst.c)), TMS(intOp(inst.d))); + { + OperandX64 opb = inst.b.kind == IrOpKind::VmReg ? luauRegAddress(vmRegOp(inst.b)) : luauConstantAddress(vmConstOp(inst.b)); + OperandX64 opc = inst.c.kind == IrOpKind::VmReg ? luauRegAddress(vmRegOp(inst.c)) : luauConstantAddress(vmConstOp(inst.c)); + callArithHelper(regs, build, vmRegOp(inst.a), opb, opc, TMS(intOp(inst.d))); break; + } case IrCmd::DO_LEN: callLengthHelper(regs, build, vmRegOp(inst.a), vmRegOp(inst.b)); break; diff --git a/CodeGen/src/IrTranslation.cpp b/CodeGen/src/IrTranslation.cpp index 91e87fdb5..76d58265b 100644 --- a/CodeGen/src/IrTranslation.cpp +++ b/CodeGen/src/IrTranslation.cpp @@ -327,13 +327,16 @@ void translateInstJumpxEqS(IrBuilder& build, const Instruction* pc, int pcpos) build.beginBlock(next); } -static void translateInstBinaryNumeric(IrBuilder& build, int ra, int rb, int rc, IrOp opc, int pcpos, TMS tm) +static void translateInstBinaryNumeric(IrBuilder& build, int ra, int rb, int rc, IrOp opb, IrOp opc, int pcpos, TMS tm) { IrOp fallback = build.block(IrBlockKind::Fallback); // fast-path: number - IrOp tb = build.inst(IrCmd::LOAD_TAG, build.vmReg(rb)); - build.inst(IrCmd::CHECK_TAG, tb, build.constTag(LUA_TNUMBER), fallback); + if (rb != -1) + { + IrOp tb = build.inst(IrCmd::LOAD_TAG, build.vmReg(rb)); + build.inst(IrCmd::CHECK_TAG, tb, build.constTag(LUA_TNUMBER), fallback); + } if (rc != -1 && rc != rb) // TODO: optimization should handle second check, but we'll test it later { @@ -341,11 +344,23 @@ static void translateInstBinaryNumeric(IrBuilder& build, int ra, int rb, int rc, build.inst(IrCmd::CHECK_TAG, tc, build.constTag(LUA_TNUMBER), fallback); } - IrOp vb = build.inst(IrCmd::LOAD_DOUBLE, build.vmReg(rb)); - IrOp vc; - + IrOp vb, vc; IrOp result; + if (opb.kind == IrOpKind::VmConst) + { + LUAU_ASSERT(build.function.proto); + TValue protok = build.function.proto->k[vmConstOp(opb)]; + + LUAU_ASSERT(protok.tt == LUA_TNUMBER); + + vb = build.constDouble(protok.value.n); + } + else + { + vb = build.inst(IrCmd::LOAD_DOUBLE, opb); + } + if (opc.kind == IrOpKind::VmConst) { LUAU_ASSERT(build.function.proto); @@ -409,18 +424,26 @@ static void translateInstBinaryNumeric(IrBuilder& build, int ra, int rb, int rc, FallbackStreamScope scope(build, fallback, next); build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + 1)); - build.inst(IrCmd::DO_ARITH, build.vmReg(ra), build.vmReg(rb), opc, build.constInt(tm)); + build.inst(IrCmd::DO_ARITH, build.vmReg(ra), opb, opc, build.constInt(tm)); build.inst(IrCmd::JUMP, next); } void translateInstBinary(IrBuilder& build, const Instruction* pc, int pcpos, TMS tm) { - translateInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), LUAU_INSN_C(*pc), build.vmReg(LUAU_INSN_C(*pc)), pcpos, tm); + translateInstBinaryNumeric( + build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), LUAU_INSN_C(*pc), build.vmReg(LUAU_INSN_B(*pc)), build.vmReg(LUAU_INSN_C(*pc)), pcpos, tm); } void translateInstBinaryK(IrBuilder& build, const Instruction* pc, int pcpos, TMS tm) { - translateInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), -1, build.vmConst(LUAU_INSN_C(*pc)), pcpos, tm); + translateInstBinaryNumeric( + build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), -1, build.vmReg(LUAU_INSN_B(*pc)), build.vmConst(LUAU_INSN_C(*pc)), pcpos, tm); +} + +void translateInstBinaryRK(IrBuilder& build, const Instruction* pc, int pcpos, TMS tm) +{ + translateInstBinaryNumeric( + build, LUAU_INSN_A(*pc), -1, LUAU_INSN_C(*pc), build.vmConst(LUAU_INSN_B(*pc)), build.vmReg(LUAU_INSN_C(*pc)), pcpos, tm); } void translateInstNot(IrBuilder& build, const Instruction* pc) diff --git a/CodeGen/src/IrTranslation.h b/CodeGen/src/IrTranslation.h index 29c143564..99c3d1c33 100644 --- a/CodeGen/src/IrTranslation.h +++ b/CodeGen/src/IrTranslation.h @@ -35,6 +35,7 @@ void translateInstJumpxEqN(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstJumpxEqS(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstBinary(IrBuilder& build, const Instruction* pc, int pcpos, TMS tm); void translateInstBinaryK(IrBuilder& build, const Instruction* pc, int pcpos, TMS tm); +void translateInstBinaryRK(IrBuilder& build, const Instruction* pc, int pcpos, TMS tm); void translateInstNot(IrBuilder& build, const Instruction* pc); void translateInstMinus(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstLength(IrBuilder& build, const Instruction* pc, int pcpos); diff --git a/Common/include/Luau/Bytecode.h b/Common/include/Luau/Bytecode.h index 36dfabdbe..e3c206701 100644 --- a/Common/include/Luau/Bytecode.h +++ b/Common/include/Luau/Bytecode.h @@ -45,7 +45,7 @@ // Version 2: Adds Proto::linedefined. Supported until 0.544. // Version 3: Adds FORGPREP/JUMPXEQK* and enhances AUX encoding for FORGLOOP. Removes FORGLOOP_NEXT/INEXT and JUMPIFEQK/JUMPIFNOTEQK. Currently supported. // Version 4: Adds Proto::flags, typeinfo, and floor division opcodes IDIV/IDIVK. Currently supported. -// Version 5: Adds vector constants. Currently supported. +// Version 5: Adds SUBRK/DIVRK and vector constants. Currently supported. // Bytecode opcode, part of the instruction header enum LuauOpcode @@ -219,7 +219,7 @@ enum LuauOpcode // ADDK, SUBK, MULK, DIVK, MODK, POWK: compute arithmetic operation between the source register and a constant and put the result into target register // A: target register // B: source register - // C: constant table index (0..255) + // C: constant table index (0..255); must refer to a number LOP_ADDK, LOP_SUBK, LOP_MULK, @@ -348,9 +348,12 @@ enum LuauOpcode // B: source register (for VAL/REF) or upvalue index (for UPVAL/UPREF) LOP_CAPTURE, - // removed in v3 - LOP_DEP_JUMPIFEQK, - LOP_DEP_JUMPIFNOTEQK, + // SUBRK, DIVRK: compute arithmetic operation between the constant and a source register and put the result into target register + // A: target register + // B: source register + // C: constant table index (0..255); must refer to a number + LOP_SUBRK, + LOP_DIVRK, // FASTCALL1: perform a fast call of a built-in function using 1 register argument // A: builtin function id (see LuauBuiltinFunction) diff --git a/Compiler/src/BytecodeBuilder.cpp b/Compiler/src/BytecodeBuilder.cpp index 8dc7b88e3..ae3766571 100644 --- a/Compiler/src/BytecodeBuilder.cpp +++ b/Compiler/src/BytecodeBuilder.cpp @@ -8,6 +8,7 @@ #include LUAU_FASTFLAG(LuauVectorLiterals) +LUAU_FASTFLAG(LuauCompileRevK) namespace Luau { @@ -1123,7 +1124,7 @@ std::string BytecodeBuilder::getError(const std::string& message) uint8_t BytecodeBuilder::getVersion() { // This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags - return (FFlag::LuauVectorLiterals ? 5 : LBC_VERSION_TARGET); + return (FFlag::LuauVectorLiterals || FFlag::LuauCompileRevK) ? 5 : LBC_VERSION_TARGET; } uint8_t BytecodeBuilder::getTypeEncodingVersion() @@ -1351,6 +1352,13 @@ void BytecodeBuilder::validateInstructions() const VCONST(LUAU_INSN_C(insn), Number); break; + case LOP_SUBRK: + case LOP_DIVRK: + VREG(LUAU_INSN_A(insn)); + VCONST(LUAU_INSN_B(insn), Number); + VREG(LUAU_INSN_C(insn)); + break; + case LOP_AND: case LOP_OR: VREG(LUAU_INSN_A(insn)); @@ -1973,6 +1981,18 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, result.append("]\n"); break; + case LOP_SUBRK: + formatAppend(result, "SUBRK R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn)); + dumpConstant(result, LUAU_INSN_B(insn)); + formatAppend(result, "] R%d\n", LUAU_INSN_C(insn)); + break; + + case LOP_DIVRK: + formatAppend(result, "DIVRK R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn)); + dumpConstant(result, LUAU_INSN_B(insn)); + formatAppend(result, "] R%d\n", LUAU_INSN_C(insn)); + break; + case LOP_AND: formatAppend(result, "AND R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); break; diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 0a5463a2f..1a6d6e5cb 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -29,6 +29,8 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) LUAU_FASTFLAGVARIABLE(LuauCompileSideEffects, false) LUAU_FASTFLAGVARIABLE(LuauCompileDeadIf, false) +LUAU_FASTFLAGVARIABLE(LuauCompileRevK, false) + namespace Luau { @@ -1516,6 +1518,20 @@ struct Compiler } else { + if (FFlag::LuauCompileRevK && (expr->op == AstExprBinary::Sub || expr->op == AstExprBinary::Div)) + { + int32_t lc = getConstantNumber(expr->left); + + if (lc >= 0 && lc <= 255) + { + uint8_t rr = compileExprAuto(expr->right, rs); + LuauOpcode op = (expr->op == AstExprBinary::Sub) ? LOP_SUBRK : LOP_DIVRK; + + bytecode.emitABC(op, target, uint8_t(lc), uint8_t(rr)); + return; + } + } + uint8_t rl = compileExprAuto(expr->left, rs); uint8_t rr = compileExprAuto(expr->right, rs); diff --git a/VM/src/lvmexecute.cpp b/VM/src/lvmexecute.cpp index 451433ee7..c172dfc72 100644 --- a/VM/src/lvmexecute.cpp +++ b/VM/src/lvmexecute.cpp @@ -101,7 +101,7 @@ VM_DISPATCH_OP(LOP_FORGLOOP), VM_DISPATCH_OP(LOP_FORGPREP_INEXT), VM_DISPATCH_OP(LOP_DEP_FORGLOOP_INEXT), VM_DISPATCH_OP(LOP_FORGPREP_NEXT), \ VM_DISPATCH_OP(LOP_NATIVECALL), VM_DISPATCH_OP(LOP_GETVARARGS), VM_DISPATCH_OP(LOP_DUPCLOSURE), VM_DISPATCH_OP(LOP_PREPVARARGS), \ VM_DISPATCH_OP(LOP_LOADKX), VM_DISPATCH_OP(LOP_JUMPX), VM_DISPATCH_OP(LOP_FASTCALL), VM_DISPATCH_OP(LOP_COVERAGE), \ - VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_DEP_JUMPIFEQK), VM_DISPATCH_OP(LOP_DEP_JUMPIFNOTEQK), VM_DISPATCH_OP(LOP_FASTCALL1), \ + VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_SUBRK), VM_DISPATCH_OP(LOP_DIVRK), VM_DISPATCH_OP(LOP_FASTCALL1), \ VM_DISPATCH_OP(LOP_FASTCALL2), VM_DISPATCH_OP(LOP_FASTCALL2K), VM_DISPATCH_OP(LOP_FORGPREP), VM_DISPATCH_OP(LOP_JUMPXEQKNIL), \ VM_DISPATCH_OP(LOP_JUMPXEQKB), VM_DISPATCH_OP(LOP_JUMPXEQKN), VM_DISPATCH_OP(LOP_JUMPXEQKS), VM_DISPATCH_OP(LOP_IDIV), \ VM_DISPATCH_OP(LOP_IDIVK), @@ -1858,9 +1858,9 @@ static void luau_execute(lua_State* L) } else if (ttisvector(rb)) { - const float* vb = rb->value.v; - float vc = cast_to(float, nvalue(kv)); - setvvalue(ra, vb[0] / vc, vb[1] / vc, vb[2] / vc, vb[3] / vc); + const float* vb = vvalue(rb); + float nc = cast_to(float, nvalue(kv)); + setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc, vb[3] / nc); VM_NEXT(); } else @@ -2697,16 +2697,53 @@ static void luau_execute(lua_State* L) LUAU_UNREACHABLE(); } - VM_CASE(LOP_DEP_JUMPIFEQK) + VM_CASE(LOP_SUBRK) { - LUAU_ASSERT(!"Unsupported deprecated opcode"); - LUAU_UNREACHABLE(); + Instruction insn = *pc++; + StkId ra = VM_REG(LUAU_INSN_A(insn)); + TValue* kv = VM_KV(LUAU_INSN_B(insn)); + StkId rc = VM_REG(LUAU_INSN_C(insn)); + + // fast-path + if (ttisnumber(rc)) + { + setnvalue(ra, nvalue(kv) - nvalue(rc)); + VM_NEXT(); + } + else + { + // slow-path, may invoke C/Lua via metamethods + VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_SUB)); + VM_NEXT(); + } } - VM_CASE(LOP_DEP_JUMPIFNOTEQK) + VM_CASE(LOP_DIVRK) { - LUAU_ASSERT(!"Unsupported deprecated opcode"); - LUAU_UNREACHABLE(); + Instruction insn = *pc++; + StkId ra = VM_REG(LUAU_INSN_A(insn)); + TValue* kv = VM_KV(LUAU_INSN_B(insn)); + StkId rc = VM_REG(LUAU_INSN_C(insn)); + + // fast-path + if (LUAU_LIKELY(ttisnumber(rc))) + { + setnvalue(ra, nvalue(kv) / nvalue(rc)); + VM_NEXT(); + } + else if (ttisvector(rc)) + { + float nb = cast_to(float, nvalue(kv)); + const float* vc = vvalue(rc); + setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2], nb / vc[3]); + VM_NEXT(); + } + else + { + // slow-path, may invoke C/Lua via metamethods + VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_DIV)); + VM_NEXT(); + } } VM_CASE(LOP_FASTCALL1) diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index c605a364c..4abf4a7c2 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -1173,6 +1173,8 @@ RETURN R0 1 TEST_CASE("AndOrChainCodegen") { + ScopedFastFlag sff("LuauCompileRevK", true); + const char* source = R"( return (1 - verticalGradientTurbulence < waterLevel + .015 and Enum.Material.Sand) @@ -1181,23 +1183,22 @@ TEST_CASE("AndOrChainCodegen") )"; CHECK_EQ("\n" + compileFunction0(source), R"( -LOADN R2 1 -GETIMPORT R3 1 [verticalGradientTurbulence] -SUB R1 R2 R3 -GETIMPORT R3 4 [waterLevel] -ADDK R2 R3 K2 [0.014999999999999999] +GETIMPORT R2 2 [verticalGradientTurbulence] +SUBRK R1 K0 [1] R2 +GETIMPORT R3 5 [waterLevel] +ADDK R2 R3 K3 [0.014999999999999999] JUMPIFNOTLT R1 R2 L0 -GETIMPORT R0 8 [Enum.Material.Sand] +GETIMPORT R0 9 [Enum.Material.Sand] JUMPIF R0 L2 -L0: GETIMPORT R1 10 [sandbank] +L0: GETIMPORT R1 11 [sandbank] LOADN R2 0 JUMPIFNOTLT R2 R1 L1 -GETIMPORT R1 10 [sandbank] +GETIMPORT R1 11 [sandbank] LOADN R2 1 JUMPIFNOTLT R1 R2 L1 -GETIMPORT R0 8 [Enum.Material.Sand] +GETIMPORT R0 9 [Enum.Material.Sand] JUMPIF R0 L2 -L1: GETIMPORT R0 12 [Enum.Material.Sandstone] +L1: GETIMPORT R0 13 [Enum.Material.Sandstone] L2: RETURN R0 1 )"); } @@ -2096,6 +2097,8 @@ RETURN R0 0 TEST_CASE("AndOrOptimizations") { + ScopedFastFlag sff("LuauCompileRevK", true); + // the OR/ORK optimization triggers for cutoff since lhs is simple CHECK_EQ("\n" + compileFunction(R"( local function advancedRidgedFilter(value, cutoff) @@ -2108,17 +2111,15 @@ end R"( ORK R2 R1 K0 [0.5] SUB R0 R0 R2 -LOADN R4 1 -LOADN R8 0 -JUMPIFNOTLT R0 R8 L0 -MINUS R7 R0 -JUMPIF R7 L1 -L0: MOVE R7 R0 -L1: MULK R6 R7 K1 [1] -LOADN R8 1 -SUB R7 R8 R2 -DIV R5 R6 R7 -SUB R3 R4 R5 +LOADN R7 0 +JUMPIFNOTLT R0 R7 L0 +MINUS R6 R0 +JUMPIF R6 L1 +L0: MOVE R6 R0 +L1: MULK R5 R6 K1 [1] +SUBRK R6 K1 [1] R2 +DIV R4 R5 R6 +SUBRK R3 K1 [1] R4 RETURN R3 1 )"); @@ -2131,9 +2132,8 @@ end 0), R"( LOADB R2 0 -LOADK R4 K0 [0.5] -MULK R5 R1 K1 [0.40000000000000002] -SUB R3 R4 R5 +MULK R4 R1 K1 [0.40000000000000002] +SUBRK R3 K0 [0.5] R4 JUMPIFNOTLT R3 R0 L1 LOADK R4 K0 [0.5] MULK R5 R1 K1 [0.40000000000000002] @@ -2153,9 +2153,8 @@ end 0), R"( LOADB R2 1 -LOADK R4 K0 [0.5] -MULK R5 R1 K1 [0.40000000000000002] -SUB R3 R4 R5 +MULK R4 R1 K1 [0.40000000000000002] +SUBRK R3 K0 [0.5] R4 JUMPIFLT R0 R3 L1 LOADK R4 K0 [0.5] MULK R5 R1 K1 [0.40000000000000002] @@ -7847,4 +7846,32 @@ RETURN R0 1 )"); } +TEST_CASE("ArithRevK") +{ + ScopedFastFlag sff("LuauCompileRevK", true); + + // - and / have special optimized form for reverse constants; in the future, + and * will likely get compiled to ADDK/MULK + // other operators are not important enough to optimize reverse constant forms for + CHECK_EQ("\n" + compileFunction0(R"( +local x: number = unknown +return 2 + x, 2 - x, 2 * x, 2 / x, 2 % x, 2 // x, 2 ^ x +)"), + R"( +GETIMPORT R0 1 [unknown] +LOADN R2 2 +ADD R1 R2 R0 +SUBRK R2 K2 [2] R0 +LOADN R4 2 +MUL R3 R4 R0 +DIVRK R4 K2 [2] R0 +LOADN R6 2 +MOD R5 R6 R0 +LOADN R7 2 +IDIV R6 R7 R0 +LOADN R8 2 +POW R7 R8 R0 +RETURN R1 7 +)"); +} + TEST_SUITE_END();