Skip to content

Commit 9d453e3

Browse files
committed
[LOL] Add support for resolve/put_to_scope
https://bugs.webkit.org/show_bug.cgi?id=305063 rdar://167709636 Reviewed by Yusuke Suzuki. This patch is somewhat complicated since the scope resolution/access bytecodes go through multiple thunks, which makes generating the correct fast paths tricky. I also had to make new operations for scope slow paths. The existing slow paths were reading values from the stack directly which we want to avoid. Although, after bug 1 below maybe that's less of an issue since we'll essentially have to flush the operands for exceptions anyway. Additionally there were a number of bugs that were uncovered now that we support enough bytecodes to trigger some interesting edge cases: 1) We have to silentSpill a def's GPR when it aliases a use. This is because the slow path could throw. This is not normally a problem but we could catch that exception in the same frame we threw from. In that case the use wouldn't have been flushed properly and we would see a stale value. 2) emitLoadCharacterString was clobbering the source and used regT1 without taking it as a parameter. I copied it and changed the behavior to avoid allocating an extra scratch register in every other case. 3) I had written the comparison operators incorrectly as for some of the constant cases they would compare backwards. 4) get_from_scope would end up clobbering the scope before jumping to the slow path, which would result in using the globalObject as the scope when it should've been a different object. Canonical link: https://commits.webkit.org/305289@main
1 parent d5114b3 commit 9d453e3

15 files changed

Lines changed: 1128 additions & 66 deletions

‎Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,6 @@
10171017
530A66C31FA3E78B0026A545 /* UnifiedSource144.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 530A66AD1FA3E7770026A545 /* UnifiedSource144.cpp */; };
10181018
530A66C41FA3E78B0026A545 /* UnifiedSource145.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 530A66B01FA3E77A0026A545 /* UnifiedSource145.cpp */; };
10191019
530A66CD1FB1346D0026A545 /* SuperSamplerBytecodeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 530A66CC1FB1346D0026A545 /* SuperSamplerBytecodeScope.h */; };
1020-
530E4FAF2E3455E000479AEE /* LOLJIT.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E4FAB2E34554A00479AEE /* LOLJIT.h */; };
10211020
530F0A982AE0607B00A0EEC0 /* LazyValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 530F0A962AE0606900A0EEC0 /* LazyValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
10221021
530FB3021E7A0B6E003C19DD /* WasmWorklist.h in Headers */ = {isa = PBXBuildFile; fileRef = 530FB3011E7A0B6E003C19DD /* WasmWorklist.h */; };
10231022
530FDE7521FAB00600059D65 /* testIncludes.m in Sources */ = {isa = PBXBuildFile; fileRef = 530FDE7321FAAFC600059D65 /* testIncludes.m */; };
@@ -1220,6 +1219,9 @@
12201219
539BFBB022AD3CDC0023F4C0 /* JSWeakObjectRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 539BFBAF22AD3CDC0023F4C0 /* JSWeakObjectRef.h */; };
12211220
539DD7F523C1BBB500905F13 /* JSArrayIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 539DD7F423C1BBA900905F13 /* JSArrayIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
12221221
539FB8BA1C99DA7C00940FA1 /* JSArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
1222+
53ADF4772F0D7A7400A05CDD /* LOLJIT.h in Headers */ = {isa = PBXBuildFile; fileRef = 53ADF46F2F0D7A2000A05CDD /* LOLJIT.h */; };
1223+
53ADF4782F0D7A7B00A05CDD /* LOLJITOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 53ADF4712F0D7A2000A05CDD /* LOLJITOperations.h */; };
1224+
53ADF4792F0D7A8000A05CDD /* LOLRegisterAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 53ADF4732F0D7A2000A05CDD /* LOLRegisterAllocator.h */; };
12231225
53B0D0C42E2E834300FC209B /* SimpleRegisterAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 53B0D0C32E2E834300FC209B /* SimpleRegisterAllocator.h */; settings = {ATTRIBUTES = (Private, ); }; };
12241226
53B4BD121F68B32500D2BEA3 /* WasmOps.h in Headers */ = {isa = PBXBuildFile; fileRef = 533B15DE1DC7F463004D500A /* WasmOps.h */; settings = {ATTRIBUTES = (Private, ); }; };
12251227
53B601EC2034B8C5006BE667 /* JSCast.h in Headers */ = {isa = PBXBuildFile; fileRef = 53B601EB2034B8C5006BE667 /* JSCast.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -4260,8 +4262,6 @@
42604262
530A66B71FA3E77D0026A545 /* UnifiedSource143.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnifiedSource143.cpp; path = "DerivedSources/JavaScriptCore/unified-sources/UnifiedSource143.cpp"; sourceTree = BUILT_PRODUCTS_DIR; };
42614263
530A66B81FA3E77E0026A545 /* UnifiedSource4-nonARC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "UnifiedSource4-nonARC.mm"; path = "DerivedSources/JavaScriptCore/unified-sources/UnifiedSource4-nonARC.mm"; sourceTree = BUILT_PRODUCTS_DIR; };
42624264
530A66CC1FB1346D0026A545 /* SuperSamplerBytecodeScope.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SuperSamplerBytecodeScope.h; sourceTree = "<group>"; };
4263-
530E4FAB2E34554A00479AEE /* LOLJIT.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LOLJIT.h; path = lol/LOLJIT.h; sourceTree = "<group>"; };
4264-
530E4FAC2E34554A00479AEE /* LOLJIT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LOLJIT.cpp; path = lol/LOLJIT.cpp; sourceTree = "<group>"; };
42654265
530F0A962AE0606900A0EEC0 /* LazyValueProfile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LazyValueProfile.h; sourceTree = "<group>"; };
42664266
530F0A972AE0606900A0EEC0 /* LazyValueProfile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LazyValueProfile.cpp; sourceTree = "<group>"; };
42674267
530FB3011E7A0B6E003C19DD /* WasmWorklist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmWorklist.h; sourceTree = "<group>"; };
@@ -4495,6 +4495,11 @@
44954495
539DD7F323C1BBA900905F13 /* JSArrayIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSArrayIterator.cpp; sourceTree = "<group>"; };
44964496
539DD7F423C1BBA900905F13 /* JSArrayIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSArrayIterator.h; sourceTree = "<group>"; };
44974497
539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSArrayInlines.h; sourceTree = "<group>"; };
4498+
53ADF46F2F0D7A2000A05CDD /* LOLJIT.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LOLJIT.h; sourceTree = "<group>"; };
4499+
53ADF4702F0D7A2000A05CDD /* LOLJIT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LOLJIT.cpp; sourceTree = "<group>"; };
4500+
53ADF4712F0D7A2000A05CDD /* LOLJITOperations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LOLJITOperations.h; sourceTree = "<group>"; };
4501+
53ADF4722F0D7A2000A05CDD /* LOLJITOperations.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LOLJITOperations.cpp; sourceTree = "<group>"; };
4502+
53ADF4732F0D7A2000A05CDD /* LOLRegisterAllocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LOLRegisterAllocator.h; sourceTree = "<group>"; };
44984503
53B0BE331E561AC900A8FC29 /* GetterSetterAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetterSetterAccessCase.cpp; sourceTree = "<group>"; };
44994504
53B0BE351E561B0900A8FC29 /* ProxyableAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProxyableAccessCase.cpp; sourceTree = "<group>"; };
45004505
53B0BE371E561B2400A8FC29 /* IntrinsicGetterAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntrinsicGetterAccessCase.cpp; sourceTree = "<group>"; };
@@ -6540,8 +6545,7 @@
65406545
45E12D8806A49B0F00E9DF84 /* jsc.cpp */,
65416546
A7C225CC139981F100FF1662 /* KeywordLookupGenerator.py */,
65426547
79D7B0E121152FD200FE7C64 /* entitlements.plist */,
6543-
530E4FAB2E34554A00479AEE /* LOLJIT.h */,
6544-
530E4FAC2E34554A00479AEE /* LOLJIT.cpp */,
6548+
53ADF4742F0D7A2000A05CDD /* lol */,
65456549
1432EBD70A34CAD400717B9F /* API */,
65466550
9688CB120ED12B4E001D649F /* assembler */,
65476551
0FEC84B21BDACD5E0080FF74 /* b3 */,
@@ -7946,6 +7950,18 @@
79467950
path = "unified-sources";
79477951
sourceTree = "<group>";
79487952
};
7953+
53ADF4742F0D7A2000A05CDD /* lol */ = {
7954+
isa = PBXGroup;
7955+
children = (
7956+
53ADF4702F0D7A2000A05CDD /* LOLJIT.cpp */,
7957+
53ADF46F2F0D7A2000A05CDD /* LOLJIT.h */,
7958+
53ADF4722F0D7A2000A05CDD /* LOLJITOperations.cpp */,
7959+
53ADF4712F0D7A2000A05CDD /* LOLJITOperations.h */,
7960+
53ADF4732F0D7A2000A05CDD /* LOLRegisterAllocator.h */,
7961+
);
7962+
path = lol;
7963+
sourceTree = "<group>";
7964+
};
79497965
53C3D5E321ECE68E0087FDFC /* testapiScripts */ = {
79507966
isa = PBXGroup;
79517967
children = (
@@ -11897,7 +11913,9 @@
1189711913
92B4EF902D71C3650068CB55 /* LLVMProfiling.h in Headers */,
1189811914
0F75A061200D26180038E2CF /* LocalAllocator.h in Headers */,
1189911915
0F75A060200D260B0038E2CF /* LocalAllocatorInlines.h in Headers */,
11900-
530E4FAF2E3455E000479AEE /* LOLJIT.h in Headers */,
11916+
53ADF4772F0D7A7400A05CDD /* LOLJIT.h in Headers */,
11917+
53ADF4782F0D7A7B00A05CDD /* LOLJITOperations.h in Headers */,
11918+
53ADF4792F0D7A8000A05CDD /* LOLRegisterAllocator.h in Headers */,
1190111919
BC18C4370E16F5CD00B34460 /* Lookup.h in Headers */,
1190211920
0F4680CD14BBB17D00BFE272 /* LowLevelInterpreter.h in Headers */,
1190311921
981ED82328234D91BAECCADE /* MachineContext.h in Headers */,

‎Source/JavaScriptCore/Sources.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ jit/JITTZoneImpls.cpp
694694
jit/Width.cpp
695695

696696
lol/LOLJIT.cpp
697+
lol/LOLJITOperations.cpp
697698

698699
llint/InPlaceInterpreter.cpp
699700
llint/LLIntCLoop.cpp

‎Source/JavaScriptCore/jit/AssemblyHelpers.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,7 @@ class AssemblyHelpers : public MacroAssembler {
18351835
return branch8(Above, AbsoluteAddress(address), TrustedImm32(blackThreshold));
18361836
}
18371837

1838+
// FIXME: We should name this something more obvious like branchIfCellIsRememberedOrEden. barrierBranch could mean many things.
18381839
// Branch taken if the cell does not need a memory fence or store barrier.
18391840
// When reverse is true, branch taken when the memory barrier or store barrier is needed.
18401841
Jump barrierBranch(VM& vm, GPRReg cell, GPRReg scratchGPR, bool reverse = false)

‎Source/JavaScriptCore/jit/BaselineJITRegisters.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ namespace ResolveScope {
119119
static constexpr GPRReg scopeGPR { GPRInfo::regT0 };
120120
static constexpr GPRReg bytecodeOffsetGPR { GPRInfo::regT3 };
121121
static constexpr GPRReg scratch1GPR { GPRInfo::regT5 };
122-
static_assert(noOverlap(metadataGPR, scopeGPR, bytecodeOffsetGPR, scratch1GPR), "Required for call to CTI thunk");
122+
static constexpr GPRReg scratch2GPR { GPRInfo::regT1 };
123+
static_assert(noOverlap(metadataGPR, scopeGPR, bytecodeOffsetGPR, scratch1GPR, scratch2GPR), "Required for call to CTI thunk");
123124
}
124125

125126
namespace GetFromScope {

‎Source/JavaScriptCore/jit/CCallHelpers.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ class CCallHelpers : public AssemblyHelpers {
671671
if constexpr (std::same_as<FirstArgumentType, CallFrame*>) {
672672
#if USE(JSVALUE64)
673673
// This only really works for 64-bit since jsvalue regs mess things up for 32-bit...
674-
static_assert(FunctionTraits<OperationType>::cCallArity() == sizeof...(Args) + 1, "Basic sanity check");
674+
static_assert(FunctionTraits<OperationType>::cCallArity() == sizeof...(Args) + 1, "Basic sanity check; Did you explicitly pass callFrameRegister for the first argument?");
675675
#endif
676676
setupArgumentsImpl<OperationType>(argSourceRegs, GPRInfo::callFrameRegister, args...);
677677
} else {

‎Source/JavaScriptCore/jit/GPRInfo.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ inline NoResultTag extractResult(NoResultTag) { return NoResult; }
900900
// We use this hack to get the GPRInfo from the GPRReg type in templates because our code is bad and we should feel bad..
901901
constexpr GPRInfo toInfoFromReg(GPRReg) { return GPRInfo(); }
902902

903+
// FIXME: We should just use a RegisterSet for this check since RegisterSet is constexpr anyway.
903904
class NoOverlapImpl {
904905
static constexpr unsigned noOverlapImplRegMask(GPRReg gpr)
905906
{

‎Source/JavaScriptCore/jit/JIT.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ namespace JSC {
291291
void compileOpStrictEqCommon(VirtualRegister src1, VirtualRegister src2);
292292
#endif
293293

294-
enum WriteBarrierMode { UnconditionalWriteBarrier, ShouldFilterBase, ShouldFilterValue, ShouldFilterBaseAndValue };
294+
enum class WriteBarrierMode { UnconditionalWriteBarrier, ShouldFilterBase, ShouldFilterValue, ShouldFilterBaseAndValue };
295+
using enum WriteBarrierMode;
295296
// value register in write barrier is used before any scratch registers
296297
// so may safely be the same as either of the scratch registers.
297298
void emitWriteBarrier(VirtualRegister owner, WriteBarrierMode);

‎Source/JavaScriptCore/jit/JITArithmetic.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,8 @@ void JIT::emit_compareAndJumpSlow(const JSInstruction* instruction, DoubleCondit
367367
emit_compareSlowImpl(op1, op2, instruction->size(), operation, iter, handleReturnValueGPR, emitCompareAndJump);
368368
}
369369

370-
template<typename SlowOperation, typename HanldeReturnValueGPRFunctor, typename EmitDoubleCompareFunctor>
371-
void JIT::emit_compareSlowImpl(VirtualRegister op1, VirtualRegister op2, size_t instructionSize, SlowOperation operation, Vector<SlowCaseEntry>::iterator& iter, const HanldeReturnValueGPRFunctor& handleReturnValueGPR, const EmitDoubleCompareFunctor& emitDoubleCompare)
370+
template<typename SlowOperation, typename HandleReturnValueGPRFunctor, typename EmitDoubleCompareFunctor>
371+
void JIT::emit_compareSlowImpl(VirtualRegister op1, VirtualRegister op2, size_t instructionSize, SlowOperation operation, Vector<SlowCaseEntry>::iterator& iter, const HandleReturnValueGPRFunctor& handleReturnValueGPR, const EmitDoubleCompareFunctor& emitDoubleCompare)
372372
{
373373

374374
// We generate inline code for the following cases in the slow path:

‎Source/JavaScriptCore/jit/JITPropertyAccess.cpp‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ void JIT::emit_op_put_getter_by_val(const JSInstruction* currentInstruction)
383383
auto bytecode = currentInstruction->as<OpPutGetterByVal>();
384384

385385
using SlowOperation = decltype(operationPutGetterByVal);
386-
constexpr GPRReg globalObjectGRP = preferredArgumentGPR<SlowOperation, 0>();
386+
constexpr GPRReg globalObjectGPR = preferredArgumentGPR<SlowOperation, 0>();
387387
constexpr GPRReg baseGPR = preferredArgumentGPR<SlowOperation, 1>();
388388
constexpr JSValueRegs propertyJSR = preferredArgumentJSR<SlowOperation, 2>();
389389
// Attributes in argument 3
@@ -393,16 +393,16 @@ void JIT::emit_op_put_getter_by_val(const JSInstruction* currentInstruction)
393393
emitGetVirtualRegister(bytecode.m_property, propertyJSR);
394394
int32_t attributes = bytecode.m_attributes;
395395
emitGetVirtualRegisterPayload(bytecode.m_accessor, setterGPR);
396-
loadGlobalObject(globalObjectGRP);
397-
callOperation(operationPutGetterByVal, globalObjectGRP, baseGPR, propertyJSR, attributes, setterGPR);
396+
loadGlobalObject(globalObjectGPR);
397+
callOperation(operationPutGetterByVal, globalObjectGPR, baseGPR, propertyJSR, attributes, setterGPR);
398398
}
399399

400400
void JIT::emit_op_put_setter_by_val(const JSInstruction* currentInstruction)
401401
{
402402
auto bytecode = currentInstruction->as<OpPutSetterByVal>();
403403

404404
using SlowOperation = decltype(operationPutSetterByVal);
405-
constexpr GPRReg globalObjectGRP = preferredArgumentGPR<SlowOperation, 0>();
405+
constexpr GPRReg globalObjectGPR = preferredArgumentGPR<SlowOperation, 0>();
406406
constexpr GPRReg baseGPR = preferredArgumentGPR<SlowOperation, 1>();
407407
constexpr JSValueRegs propertyJSR = preferredArgumentJSR<SlowOperation, 2>();
408408
// Attributes in argument 3
@@ -412,8 +412,8 @@ void JIT::emit_op_put_setter_by_val(const JSInstruction* currentInstruction)
412412
emitGetVirtualRegister(bytecode.m_property, propertyJSR);
413413
int32_t attributes = bytecode.m_attributes;
414414
emitGetVirtualRegisterPayload(bytecode.m_accessor, setterGPR);
415-
loadGlobalObject(globalObjectGRP);
416-
callOperation(operationPutSetterByVal, globalObjectGRP, baseGPR, propertyJSR, attributes, setterGPR);
415+
loadGlobalObject(globalObjectGPR);
416+
callOperation(operationPutSetterByVal, globalObjectGPR, baseGPR, propertyJSR, attributes, setterGPR);
417417
}
418418

419419
void JIT::emit_op_del_by_id(const JSInstruction* currentInstruction)
@@ -973,6 +973,8 @@ void JIT::emit_op_resolve_scope(const JSInstruction* currentInstruction)
973973
auto resolveTypeAddress = metadataAddress.withOffset(Metadata::offsetOfResolveType());
974974
auto globalLexicalBindingEpochAddress = metadataAddress.withOffset(Metadata::offsetOfGlobalLexicalBindingEpoch());
975975

976+
// FIXME: This code is weird when caching fails because it goes to a slow path that will check the exact same condition before falling into the C++ slow path.
977+
// It's unclear if that makes a meaningful difference for perf but we should consider doing something smarter.
976978
switch (profiledResolveType) {
977979
case GlobalProperty: {
978980
addSlowCase(branch32(NotEqual, resolveTypeAddress, TrustedImm32(profiledResolveType)));
@@ -1044,6 +1046,7 @@ void JIT::emitSlow_op_resolve_scope(const JSInstruction* currentInstruction, Vec
10441046
addPtr(TrustedImm32(m_profiledCodeBlock->metadataTable()->offsetInMetadataTable(bytecode)), GPRInfo::metadataTableRegister, metadataGPR);
10451047

10461048
MacroAssemblerCodeRef<JITThunkPtrTag> code;
1049+
// FIXME: Why do we generate the cases for the thunks we already emitted in the fast path. It seems like those should just go straight to the generic slow path thunk.
10471050
if (profiledResolveType == ClosureVarWithVarInjectionChecks)
10481051
code = vm().getCTIStub(generateOpResolveScopeThunk<ClosureVarWithVarInjectionChecks>);
10491052
else if (profiledResolveType == GlobalVar)

‎Source/JavaScriptCore/jit/RegisterSet.h‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
#include <wtf/BitSet.h>
3939
#include <wtf/CommaPrinter.h>
4040

41+
#include <ranges>
42+
4143
namespace JSC {
4244

4345
class ScalarRegisterSet;
@@ -63,6 +65,14 @@ class RegisterSetBuilder final {
6365
setMany(regs...);
6466
}
6567

68+
static RegisterSetBuilder fromIterable(const std::ranges::range auto& regs)
69+
{
70+
RegisterSetBuilder result;
71+
for (auto reg : regs)
72+
result.setAny(reg);
73+
return result;
74+
}
75+
6676
inline constexpr RegisterSetBuilder& add(Reg reg, Width width)
6777
{
6878
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);

0 commit comments

Comments
 (0)