Bug 1677580 - Remove JSOp::Def{Var,Let,Const,Fun} opcodes. r=jandem

These are no longer generated and their behaviour is subsumed by the
GlobalOrEvalDeclInstantiation.

Differential Revision: https://phabricator.services.mozilla.com/D97215
This commit is contained in:
Ted Campbell 2020-11-20 14:43:14 +00:00
Родитель 23e9698e18
Коммит b85ecde81f
15 изменённых файлов: 4 добавлений и 535 удалений

Просмотреть файл

@ -1373,10 +1373,6 @@ static bool BytecodeIsEffectful(JSOp op) {
case JSOp::InitAliasedLexical:
case JSOp::SetIntrinsic:
case JSOp::InitGLexical:
case JSOp::DefVar:
case JSOp::DefLet:
case JSOp::DefConst:
case JSOp::DefFun:
case JSOp::GlobalOrEvalDeclInstantiation:
case JSOp::SetFunName:
case JSOp::MutateProto:

Просмотреть файл

@ -3857,66 +3857,6 @@ bool BaselineCodeGen<Handler>::emit_SetIntrinsic() {
return callVM<Fn, SetIntrinsicOperation>();
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_DefVar() {
frame.syncStack(0);
masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
prepareVMCall();
pushBytecodePCArg();
pushScriptArg();
pushArg(R0.scratchReg());
using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*);
return callVM<Fn, DefVarOperation>();
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emitDefLexical(JSOp op) {
MOZ_ASSERT(op == JSOp::DefConst || op == JSOp::DefLet);
frame.syncStack(0);
masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
prepareVMCall();
pushBytecodePCArg();
pushScriptArg();
pushArg(R0.scratchReg());
using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*);
return callVM<Fn, DefLexicalOperation>();
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_DefConst() {
return emitDefLexical(JSOp::DefConst);
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_DefLet() {
return emitDefLexical(JSOp::DefLet);
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_DefFun() {
frame.popRegsAndSync(1);
masm.unboxObject(R0, R0.scratchReg());
masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
prepareVMCall();
pushArg(R0.scratchReg());
pushArg(R1.scratchReg());
pushScriptArg();
using Fn = bool (*)(JSContext*, HandleScript, HandleObject, HandleFunction);
return callVM<Fn, DefFunOperation>();
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_GlobalOrEvalDeclInstantiation() {
frame.syncStack(0);

Просмотреть файл

@ -231,7 +231,6 @@ class BaselineCodeGen {
MOZ_MUST_USE bool emitSetPropSuper(bool strict);
MOZ_MUST_USE bool emitBindName(JSOp op);
MOZ_MUST_USE bool emitDefLexical(JSOp op);
// Try to bake in the result of GETGNAME/BINDGNAME instead of using an IC.
// Return true if we managed to optimize the op.

Просмотреть файл

@ -266,10 +266,6 @@ IonBytecodeInfo js::jit::AnalyzeBytecodeForIon(JSContext* cx,
case JSOp::SetAliasedVar:
case JSOp::Lambda:
case JSOp::LambdaArrow:
case JSOp::DefFun:
case JSOp::DefVar:
case JSOp::DefLet:
case JSOp::DefConst:
case JSOp::PushLexicalEnv:
case JSOp::PopLexicalEnv:
case JSOp::ImplicitThis:

Просмотреть файл

@ -6532,46 +6532,6 @@ void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) {
masm.bind(ool->rejoin());
}
void CodeGenerator::visitDefVar(LDefVar* lir) {
Register envChain = ToRegister(lir->environmentChain());
JSScript* script = current->mir()->info().script();
jsbytecode* pc = lir->mir()->resumePoint()->pc();
pushArg(ImmPtr(pc)); // jsbytecode*
pushArg(ImmGCPtr(script)); // JSScript*
pushArg(envChain); // JSObject*
using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*);
callVM<Fn, DefVarOperation>(lir);
}
void CodeGenerator::visitDefLexical(LDefLexical* lir) {
Register envChain = ToRegister(lir->environmentChain());
JSScript* script = current->mir()->info().script();
jsbytecode* pc = lir->mir()->resumePoint()->pc();
pushArg(ImmPtr(pc)); // jsbytecode*
pushArg(ImmGCPtr(script)); // JSScript*
pushArg(envChain); // JSObject*
using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*);
callVM<Fn, DefLexicalOperation>(lir);
}
void CodeGenerator::visitDefFun(LDefFun* lir) {
Register envChain = ToRegister(lir->environmentChain());
Register fun = ToRegister(lir->fun());
pushArg(fun);
pushArg(envChain);
pushArg(ImmGCPtr(current->mir()->info().script()));
using Fn = bool (*)(JSContext*, HandleScript, HandleObject, HandleFunction);
callVM<Fn, DefFunOperation>(lir);
}
void CodeGenerator::visitCheckOverRecursedFailure(
CheckOverRecursedFailure* ool) {
// The OOL path is hit if the recursion depth has been exceeded.

Просмотреть файл

@ -128,30 +128,6 @@ void LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed* ins) {
assignSafepoint(lir, ins);
}
void LIRGenerator::visitDefVar(MDefVar* ins) {
LDefVar* lir =
new (alloc()) LDefVar(useRegisterAtStart(ins->environmentChain()));
add(lir, ins);
assignSafepoint(lir, ins);
}
void LIRGenerator::visitDefLexical(MDefLexical* ins) {
LDefLexical* lir =
new (alloc()) LDefLexical(useRegisterAtStart(ins->environmentChain()));
add(lir, ins);
assignSafepoint(lir, ins);
}
void LIRGenerator::visitDefFun(MDefFun* ins) {
MDefinition* fun = ins->fun();
MOZ_ASSERT(fun->type() == MIRType::Object);
LDefFun* lir = new (alloc()) LDefFun(
useRegisterAtStart(fun), useRegisterAtStart(ins->environmentChain()));
add(lir, ins);
assignSafepoint(lir, ins);
}
void LIRGenerator::visitNewArray(MNewArray* ins) {
LNewArray* lir = new (alloc()) LNewArray(temp());
define(lir, ins);

Просмотреть файл

@ -6799,46 +6799,6 @@ class MGlobalDeclInstantiation : public MNullaryInstruction {
TRIVIAL_NEW_WRAPPERS
};
// If not defined, set a global variable to |undefined|.
class MDefVar : public MUnaryInstruction, public NoTypePolicy::Data {
private:
explicit MDefVar(MDefinition* envChain)
: MUnaryInstruction(classOpcode, envChain) {}
public:
INSTRUCTION_HEADER(DefVar)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, environmentChain))
bool possiblyCalls() const override { return true; }
};
class MDefLexical : public MUnaryInstruction, public NoTypePolicy::Data {
private:
explicit MDefLexical(MDefinition* envChain)
: MUnaryInstruction(classOpcode, envChain) {}
public:
INSTRUCTION_HEADER(DefLexical)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, environmentChain))
bool possiblyCalls() const override { return true; }
};
class MDefFun : public MBinaryInstruction, public ObjectPolicy<0>::Data {
private:
MDefFun(MDefinition* fun, MDefinition* envChain)
: MBinaryInstruction(classOpcode, fun, envChain) {}
public:
INSTRUCTION_HEADER(DefFun)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, fun), (1, environmentChain))
bool possiblyCalls() const override { return true; }
};
class MRegExp : public MNullaryInstruction {
CompilerGCPointer<RegExpObject*> source_;
bool hasShared_;

Просмотреть файл

@ -105,9 +105,6 @@ namespace jit {
_(DebugLeaveThenRecreateLexicalEnv, \
js::jit::DebugLeaveThenRecreateLexicalEnv) \
_(Debug_CheckSelfHosted, js::Debug_CheckSelfHosted) \
_(DefFunOperation, js::DefFunOperation) \
_(DefLexicalOperation, js::DefLexicalOperation) \
_(DefVarOperation, js::DefVarOperation) \
_(DelElemOperationNonStrict, js::DelElemOperation<false>) \
_(DelElemOperationStrict, js::DelElemOperation<true>) \
_(DelPropOperationNonStrict, js::DelPropOperation<false>) \

Просмотреть файл

@ -1530,42 +1530,6 @@ bool WarpBuilder::usesEnvironmentChain() const {
return script_->jitScript()->usesEnvironmentChain();
}
bool WarpBuilder::build_DefVar(BytecodeLocation loc) {
MOZ_ASSERT(usesEnvironmentChain());
MDefinition* env = current->environmentChain();
MDefVar* defvar = MDefVar::New(alloc(), env);
current->add(defvar);
return resumeAfter(defvar, loc);
}
bool WarpBuilder::buildDefLexicalOp(BytecodeLocation loc) {
MOZ_ASSERT(usesEnvironmentChain());
MDefinition* env = current->environmentChain();
MDefLexical* defLexical = MDefLexical::New(alloc(), env);
current->add(defLexical);
return resumeAfter(defLexical, loc);
}
bool WarpBuilder::build_DefLet(BytecodeLocation loc) {
return buildDefLexicalOp(loc);
}
bool WarpBuilder::build_DefConst(BytecodeLocation loc) {
return buildDefLexicalOp(loc);
}
bool WarpBuilder::build_DefFun(BytecodeLocation loc) {
MOZ_ASSERT(usesEnvironmentChain());
MDefinition* fun = current->pop();
MDefinition* env = current->environmentChain();
MDefFun* deffun = MDefFun::New(alloc(), fun, env);
current->add(deffun);
return resumeAfter(deffun, loc);
}
bool WarpBuilder::build_GlobalOrEvalDeclInstantiation(BytecodeLocation loc) {
MOZ_ASSERT(!script_->isForEval(), "Eval scripts not supported");
auto* redeclCheck = MGlobalDeclInstantiation::New(alloc());

Просмотреть файл

@ -303,7 +303,6 @@ class MOZ_STACK_CLASS WarpBuilder : public WarpBuilderShared {
MOZ_MUST_USE bool buildBinaryOp(BytecodeLocation loc);
MOZ_MUST_USE bool buildCompareOp(BytecodeLocation loc);
MOZ_MUST_USE bool buildTestOp(BytecodeLocation loc);
MOZ_MUST_USE bool buildDefLexicalOp(BytecodeLocation loc);
MOZ_MUST_USE bool buildCallOp(BytecodeLocation loc);
MOZ_MUST_USE bool buildInitPropGetterSetterOp(BytecodeLocation loc);

Просмотреть файл

@ -660,10 +660,6 @@ AbortReasonOr<WarpScriptSnapshot*> WarpScriptOracle::createScriptSnapshot() {
case JSOp::DynamicImport:
case JSOp::Not:
case JSOp::ToString:
case JSOp::DefVar:
case JSOp::DefLet:
case JSOp::DefConst:
case JSOp::DefFun:
case JSOp::GlobalOrEvalDeclInstantiation:
case JSOp::BindVar:
case JSOp::MutateProto:

Просмотреть файл

@ -662,47 +662,6 @@ class LWasmInterruptCheck : public LInstructionHelper<0, 1, 0> {
const LAllocation* tlsPtr() { return getOperand(0); }
};
class LDefVar : public LCallInstructionHelper<0, 1, 0> {
public:
LIR_HEADER(DefVar)
explicit LDefVar(const LAllocation& envChain)
: LCallInstructionHelper(classOpcode) {
setOperand(0, envChain);
}
const LAllocation* environmentChain() { return getOperand(0); }
MDefVar* mir() const { return mir_->toDefVar(); }
};
class LDefLexical : public LCallInstructionHelper<0, 1, 0> {
public:
LIR_HEADER(DefLexical)
explicit LDefLexical(const LAllocation& envChain)
: LCallInstructionHelper(classOpcode) {
setOperand(0, envChain);
}
const LAllocation* environmentChain() { return getOperand(0); }
MDefLexical* mir() const { return mir_->toDefLexical(); }
};
class LDefFun : public LCallInstructionHelper<0, 2, 0> {
public:
LIR_HEADER(DefFun)
LDefFun(const LAllocation& fun, const LAllocation& envChain)
: LCallInstructionHelper(classOpcode) {
setOperand(0, fun);
setOperand(1, envChain);
}
const LAllocation* fun() { return getOperand(0); }
const LAllocation* environmentChain() { return getOperand(1); }
MDefFun* mir() const { return mir_->toDefFun(); }
};
class LTypeOfV : public LInstructionHelper<1, BOX_PIECES, 1> {
public:
LIR_HEADER(TypeOfV)

Просмотреть файл

@ -3722,39 +3722,6 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
}
END_CASE(SetLocal)
CASE(DefVar) {
HandleObject env = REGS.fp()->environmentChain();
if (!DefVarOperation(cx, env, script, REGS.pc)) {
goto error;
}
}
END_CASE(DefVar)
CASE(DefConst)
CASE(DefLet) {
HandleObject env = REGS.fp()->environmentChain();
if (!DefLexicalOperation(cx, env, script, REGS.pc)) {
goto error;
}
}
END_CASE(DefLet)
CASE(DefFun) {
/*
* A top-level function defined in Global or Eval code (see ECMA-262
* Ed. 3), or else a SpiderMonkey extension: a named function statement
* in a compound statement (not at the top statement level of global
* code, or at the top level of a function body).
*/
ReservedRooted<JSFunction*> fun(&rootFunction0,
&REGS.sp[-1].toObject().as<JSFunction>());
if (!DefFunOperation(cx, script, REGS.fp()->environmentChain(), fun)) {
goto error;
}
REGS.sp--;
}
END_CASE(DefFun)
CASE(GlobalOrEvalDeclInstantiation) {
GCThingIndex lastFun = GET_GCTHING_INDEX(REGS.pc);
HandleObject env = REGS.fp()->environmentChain();
@ -4713,175 +4680,6 @@ JSObject* js::BindVarOperation(JSContext* cx, JSObject* envChain) {
return &GetVariablesObject(envChain);
}
bool js::DefVarOperation(JSContext* cx, HandleObject envChain,
HandleScript script, jsbytecode* pc) {
MOZ_ASSERT(JSOp(*pc) == JSOp::DefVar);
RootedObject varobj(cx, &GetVariablesObject(envChain));
MOZ_ASSERT(varobj->isQualifiedVarObj());
RootedPropertyName name(cx, script->getName(pc));
unsigned attrs = JSPROP_ENUMERATE;
if (!script->isForEval()) {
attrs |= JSPROP_PERMANENT;
}
#ifdef DEBUG
// Per spec, it is an error to redeclare a lexical binding. This should
// have already been checked.
if (JS_HasExtensibleLexicalEnvironment(varobj)) {
Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
lexicalEnv = &JS_ExtensibleLexicalEnvironment(varobj)
->as<LexicalEnvironmentObject>();
MOZ_ASSERT(CheckVarNameConflict(cx, lexicalEnv, name));
}
#endif
Rooted<PropertyResult> prop(cx);
RootedObject obj2(cx);
if (!LookupProperty(cx, varobj, name, &obj2, &prop)) {
return false;
}
/* Steps 8c, 8d. */
if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
if (!DefineDataProperty(cx, varobj, name, UndefinedHandleValue, attrs)) {
return false;
}
}
if (varobj->is<GlobalObject>()) {
if (!varobj->as<GlobalObject>().realm()->addToVarNames(cx, name)) {
return false;
}
}
return true;
}
bool js::DefLexicalOperation(JSContext* cx, HandleObject envChain,
HandleScript script, jsbytecode* pc) {
MOZ_ASSERT(JSOp(*pc) == JSOp::DefLet || JSOp(*pc) == JSOp::DefConst);
unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
if (JSOp(*pc) == JSOp::DefConst) {
attrs |= JSPROP_READONLY;
}
Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
if (script->hasNonSyntacticScope()) {
lexicalEnv = &NearestEnclosingExtensibleLexicalEnvironment(envChain);
} else {
lexicalEnv = &cx->global()->lexicalEnvironment();
}
#ifdef DEBUG
RootedObject varObj(cx);
if (script->hasNonSyntacticScope()) {
varObj = &GetVariablesObject(envChain);
} else {
varObj = cx->global();
}
MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
lexicalEnv == &cx->global()->lexicalEnvironment() &&
varObj == cx->global());
// Redeclaration checks should have already been done.
RootedPropertyName name(cx, script->getName(pc));
MOZ_ASSERT(CheckLexicalNameConflict(cx, lexicalEnv, varObj, name));
#endif
RootedId id(cx, NameToId(script->getName(pc)));
RootedValue uninitialized(cx, MagicValue(JS_UNINITIALIZED_LEXICAL));
return NativeDefineDataProperty(cx, lexicalEnv, id, uninitialized, attrs);
}
bool js::DefFunOperation(JSContext* cx, HandleScript script,
HandleObject envChain, HandleFunction fun) {
/*
* We define the function as a property of the variable object and not the
* current scope chain even for the case of function expression statements
* and functions defined by eval inside let or with blocks.
*/
RootedObject parent(cx, envChain);
while (!parent->isQualifiedVarObj()) {
parent = parent->enclosingEnvironment();
}
/* ES5 10.5 (NB: with subsequent errata). */
RootedPropertyName name(cx, fun->explicitName()->asPropertyName());
Rooted<PropertyResult> prop(cx);
RootedObject pobj(cx);
if (!LookupProperty(cx, parent, name, &pobj, &prop)) {
return false;
}
RootedValue rval(cx, ObjectValue(*fun));
/*
* ECMA requires functions defined when entering Eval code to be
* impermanent.
*/
unsigned attrs = script->isForEval() ? JSPROP_ENUMERATE
: JSPROP_ENUMERATE | JSPROP_PERMANENT;
/* Steps 5d, 5f. */
if (!prop || pobj != parent) {
if (!DefineDataProperty(cx, parent, name, rval, attrs)) {
return false;
}
if (parent->is<GlobalObject>()) {
return parent->as<GlobalObject>().realm()->addToVarNames(cx, name);
}
return true;
}
/*
* Step 5e.
*
* A DebugEnvironmentProxy is okay here, and sometimes necessary. If
* Debugger.Frame.prototype.eval defines a function with the same name as an
* extant variable in the frame, the DebugEnvironmentProxy takes care of
* storing the function in the stack frame (for non-aliased variables) or on
* the scope object (for aliased).
*/
MOZ_ASSERT(parent->isNative() || parent->is<DebugEnvironmentProxy>());
if (parent->is<GlobalObject>()) {
Shape* shape = prop.shape();
if (shape->configurable()) {
if (!DefineDataProperty(cx, parent, name, rval, attrs)) {
return false;
}
} else {
MOZ_ASSERT(shape->isDataDescriptor());
MOZ_ASSERT(shape->writable());
MOZ_ASSERT(shape->enumerable());
}
// Careful: the presence of a shape, even one appearing to derive from
// a variable declaration, doesn't mean it's in [[VarNames]].
if (!parent->as<GlobalObject>().realm()->addToVarNames(cx, name)) {
return false;
}
}
/*
* Non-global properties, and global properties which we aren't simply
* redefining, must be set. First, this preserves their attributes.
* Second, this will produce warnings and/or errors as necessary if the
* specified Call object property is not writable (const).
*/
/* Step 5f. */
RootedId id(cx, NameToId(name));
return PutProperty(cx, parent, id, rval, script->strict());
}
JSObject* js::ImportMetaOperation(JSContext* cx, HandleScript script) {
RootedObject module(cx, GetModuleObjectForScript(script));
MOZ_ASSERT(module);

Просмотреть файл

@ -581,15 +581,6 @@ bool DelElemOperation(JSContext* cx, HandleValue val, HandleValue index,
JSObject* BindVarOperation(JSContext* cx, JSObject* envChain);
bool DefVarOperation(JSContext* cx, HandleObject envChain, HandleScript script,
jsbytecode* pc);
bool DefLexicalOperation(JSContext* cx, HandleObject envChain,
HandleScript script, jsbytecode* pc);
bool DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain,
HandleFunction funArg);
JSObject* SingletonObjectLiteralOperation(JSContext* cx, HandleScript script,
jsbytecode* pc);

Просмотреть файл

@ -3326,72 +3326,6 @@
* Stack: => env
*/ \
MACRO(BindVar, bind_var, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Create a new binding on the current VariableEnvironment (the environment
* on the environment chain designated to receive new variables).
*
* `JSOp::Def{Var,Let,Const,Fun}` instructions must appear in the script
* before anything else that might add bindings to the environment, and
* only once per binding. There must be a correct entry for the new binding
* in `script->bodyScope()`. (All this ensures that at run time, there is
* no existing conflicting binding. This is checked by the
* `JSOp::GlobalOrEvalDeclInstantiation` bytecode instruction that must
* appear before `JSOp::Def{Var,Let,Const,Fun}`.)
*
* Throw a SyntaxError if the current VariableEnvironment is the global
* environment and a binding with the same name exists on the global
* lexical environment.
*
* This is used for global scripts and also in some cases for function
* scripts where use of dynamic scoping inhibits optimization.
*
* Category: Variables and scopes
* Type: Creating and deleting bindings
* Operands: uint32_t nameIndex
* Stack: =>
*/ \
MACRO(DefVar, def_var, NULL, 5, 0, 0, JOF_ATOM) \
/*
* Create a new binding for the given function on the current scope.
*
* `fun` must be a function object with an explicit name. The new
* variable's name is `fun->explicitName()`, and its value is `fun`. In
* global scope, this creates a new property on the global object.
*
* Implements: The body of the loop in [GlobalDeclarationInstantiation][1]
* step 17 ("For each Parse Node *f* in *functionsToInitialize*...") and
* the corresponding loop in [EvalDeclarationInstantiation][2].
*
* [1]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
* [2]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
*
* Category: Variables and scopes
* Type: Creating and deleting bindings
* Operands:
* Stack: fun =>
*/ \
MACRO(DefFun, def_fun, NULL, 1, 1, 0, JOF_BYTE) \
/*
* Create a new uninitialized mutable binding in the global lexical
* environment. Throw a SyntaxError if a binding with the same name already
* exists on that environment, or if a var binding with the same name
* exists on the global.
*
* Category: Variables and scopes
* Type: Creating and deleting bindings
* Operands: uint32_t nameIndex
* Stack: =>
*/ \
MACRO(DefLet, def_let, NULL, 5, 0, 0, JOF_ATOM) \
/*
* Like `DefLet`, but create an uninitialized constant binding.
*
* Category: Variables and scopes
* Type: Creating and deleting bindings
* Operands: uint32_t nameIndex
* Stack: =>
*/ \
MACRO(DefConst, def_const, NULL, 5, 0, 0, JOF_ATOM) \
/*
* Check for conflicting bindings and then initialize them in global or
* sloppy eval scripts. This is required for global scripts with any
@ -3685,6 +3619,10 @@
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(MACRO) \
MACRO(235) \
MACRO(236) \
MACRO(237) \
MACRO(238) \
MACRO(239) \
MACRO(240) \
MACRO(241) \