Skip to content

Commit 911eb9b

Browse files
committed
Implement jit for atomic load/store
Extract atomics operations from TypedArray.cpp in AtomicsOperations.cpp then share the atomics code for TypedArray and Wasm Add encoding of CMPXCHG8B which has 2 dst and 5 sources
1 parent 9dfe37f commit 911eb9b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+913
-394
lines changed

lib/Backend/GlobOpt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15975,6 +15975,7 @@ GlobOpt::PreLowerCanonicalize(IR::Instr *instr, Value **pSrc1Val, Value **pSrc2V
1597515975
case Js::OpCode::TrapIfMinIntOverNegOne:
1597615976
case Js::OpCode::TrapIfTruncOverflow:
1597715977
case Js::OpCode::TrapIfZero:
15978+
case Js::OpCode::TrapIfUnalignedAccess:
1597815979
case Js::OpCode::FromVar:
1597915980
case Js::OpCode::Conv_Prim:
1598015981
case Js::OpCode::LdC_A_I4:

lib/Backend/GlobOptExpr.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,28 @@ GlobOpt::OptimizeChecks(IR::Instr * const instr)
435435
}
436436
break;
437437
}
438+
case Js::OpCode::TrapIfUnalignedAccess:
439+
if (src1 && src1->IsImmediateOpnd())
440+
{
441+
int64 val = src1->GetImmediateValue(func);
442+
Assert(src2->IsImmediateOpnd());
443+
uint32 cmpValue = (uint32)src2->GetImmediateValue(func);
444+
uint32 mask = src2->GetSize() - 1;
445+
Assert((cmpValue & ~mask) == 0);
446+
447+
if (((uint32)val & mask) == cmpValue)
448+
{
449+
instr->FreeSrc2();
450+
instr->m_opcode = Js::OpCode::Ld_I4;
451+
}
452+
else
453+
{
454+
TransformIntoUnreachable(WASMERR_UnalignedAtomicAccess, instr);
455+
InsertByteCodeUses(instr);
456+
RemoveCodeAfterNoFallthroughInstr(instr); //remove dead code
457+
}
458+
}
459+
break;
438460
default:
439461
return;
440462
}

lib/Backend/IRBuilderAsmJs.cpp

Lines changed: 37 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,64 +1421,20 @@ IRBuilderAsmJs::BuildStartCall(Js::OpCodeAsmJs newOpcode, uint32 offset)
14211421
void
14221422
IRBuilderAsmJs::InitializeMemAccessTypeInfo(Js::ArrayBufferView::ViewType viewType, _Out_ MemAccessTypeInfo * typeInfo)
14231423
{
1424-
typeInfo->type = TyInt32;
1425-
typeInfo->valueRegType = WAsmJs::INT32;
1426-
1424+
AssertOrFailFast(typeInfo);
1425+
14271426
switch (viewType)
14281427
{
1429-
case Js::ArrayBufferView::TYPE_INT8_TO_INT64:
1430-
typeInfo->valueRegType = WAsmJs::INT64;
1431-
case Js::ArrayBufferView::TYPE_INT8:
1432-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int8Array);
1433-
typeInfo->type = TyInt8;
1434-
break;
1435-
case Js::ArrayBufferView::TYPE_UINT8_TO_INT64:
1436-
typeInfo->valueRegType = WAsmJs::INT64;
1437-
case Js::ArrayBufferView::TYPE_UINT8:
1438-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Uint8Array);
1439-
typeInfo->type = TyUint8;
1440-
break;
1441-
case Js::ArrayBufferView::TYPE_INT16_TO_INT64:
1442-
typeInfo->valueRegType = WAsmJs::INT64;
1443-
case Js::ArrayBufferView::TYPE_INT16:
1444-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int16Array);
1445-
typeInfo->type = TyInt16;
1446-
break;
1447-
case Js::ArrayBufferView::TYPE_UINT16_TO_INT64:
1448-
typeInfo->valueRegType = WAsmJs::INT64;
1449-
case Js::ArrayBufferView::TYPE_UINT16:
1450-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Uint16Array);
1451-
typeInfo->type = TyUint16;
1452-
break;
1453-
case Js::ArrayBufferView::TYPE_INT32_TO_INT64:
1454-
typeInfo->valueRegType = WAsmJs::INT64;
1455-
case Js::ArrayBufferView::TYPE_INT32:
1456-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int32Array);
1457-
typeInfo->type = TyInt32;
1458-
break;
1459-
case Js::ArrayBufferView::TYPE_UINT32_TO_INT64:
1460-
typeInfo->valueRegType = WAsmJs::INT64;
1461-
case Js::ArrayBufferView::TYPE_UINT32:
1462-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Uint32Array);
1463-
typeInfo->type = TyUint32;
1464-
break;
1465-
case Js::ArrayBufferView::TYPE_FLOAT32:
1466-
typeInfo->valueRegType = WAsmJs::FLOAT32;
1467-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Float32Array);
1468-
typeInfo->type = TyFloat32;
1469-
break;
1470-
case Js::ArrayBufferView::TYPE_FLOAT64:
1471-
typeInfo->valueRegType = WAsmJs::FLOAT64;
1472-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Float64Array);
1473-
typeInfo->type = TyFloat64;
1474-
break;
1475-
case Js::ArrayBufferView::TYPE_INT64:
1476-
typeInfo->valueRegType = WAsmJs::INT64;
1477-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int64Array);
1478-
typeInfo->type = TyInt64;
1428+
#define ARRAYBUFFER_VIEW(name, align, RegType, MemType, irSuffix) \
1429+
case Js::ArrayBufferView::TYPE_##name: \
1430+
typeInfo->valueRegType = WAsmJs::FromPrimitiveType<RegType>(); \
1431+
typeInfo->type = Ty##irSuffix;\
1432+
typeInfo->arrayType = ValueType::GetObject(ObjectType::##irSuffix##Array); \
1433+
Assert(TySize[Ty##irSuffix] == (1<<align)); \
14791434
break;
1435+
#include "Language/AsmJsArrayBufferViews.h"
14801436
default:
1481-
Assume(UNREACHED);
1437+
AssertOrFailFast(UNREACHED);
14821438
}
14831439
}
14841440

@@ -1494,11 +1450,15 @@ IRBuilderAsmJs::BuildWasmMemAccess(Js::OpCodeAsmJs newOpcode, uint32 offset)
14941450
void
14951451
IRBuilderAsmJs::BuildWasmMemAccess(Js::OpCodeAsmJs newOpcode, uint32 offset, uint32 slotIndex, Js::RegSlot value, uint32 constOffset, Js::ArrayBufferView::ViewType viewType)
14961452
{
1497-
bool isLd = newOpcode == Js::OpCodeAsmJs::LdArrWasm;
1498-
Js::OpCode op = isLd ? Js::OpCode::LdArrViewElemWasm : Js::OpCode::StArrViewElem;
1453+
bool isAtomic = newOpcode == Js::OpCodeAsmJs::StArrAtomic || newOpcode == Js::OpCodeAsmJs::LdArrAtomic;
1454+
bool isLd = newOpcode == Js::OpCodeAsmJs::LdArrWasm || newOpcode == Js::OpCodeAsmJs::LdArrAtomic;
1455+
Js::OpCode op = isAtomic ?
1456+
isLd ? Js::OpCode::LdAtomicWasm : Js::OpCode::StAtomicWasm
1457+
: isLd ? Js::OpCode::LdArrViewElemWasm : Js::OpCode::StArrViewElem;
14991458

15001459
MemAccessTypeInfo typeInfo;
15011460
InitializeMemAccessTypeInfo(viewType, &typeInfo);
1461+
const uint32 memAccessSize = TySize[typeInfo.type];
15021462

15031463
Js::RegSlot valueRegSlot = GetRegSlotFromTypedReg(value, typeInfo.valueRegType);
15041464
IR::Instr * instr = nullptr;
@@ -1507,6 +1467,22 @@ IRBuilderAsmJs::BuildWasmMemAccess(Js::OpCodeAsmJs newOpcode, uint32 offset, uin
15071467

15081468
Js::RegSlot indexRegSlot = GetRegSlotFromIntReg(slotIndex);
15091469
IR::RegOpnd * indexOpnd = BuildSrcOpnd(indexRegSlot, TyUint32);
1470+
if (isAtomic && memAccessSize > 1)
1471+
{
1472+
const uint32 mask = memAccessSize - 1;
1473+
// We need (constOffset + index) & mask == 0
1474+
// Since we know constOffset ahead of time
1475+
// what we need to check is index & mask == (memAccessSize - (constOffset & mask)) & mask
1476+
const uint32 offseted = constOffset & mask;
1477+
// In this IntContOpnd, the value is what the index&mask should be, the type carries the size of the access
1478+
IR::Opnd* offsetedOpnd = IR::IntConstOpnd::NewFromType((memAccessSize - offseted) & mask, typeInfo.type, m_func);
1479+
IR::RegOpnd* intermediateIndex = IR::RegOpnd::New(TyUint32, m_func);
1480+
instr = IR::Instr::New(Js::OpCode::TrapIfUnalignedAccess, intermediateIndex, indexOpnd, offsetedOpnd, m_func);
1481+
AddInstr(instr, offset);
1482+
1483+
// Create dependency between load/store and trap through the index
1484+
indexOpnd = intermediateIndex;
1485+
}
15101486
indirOpnd = IR::IndirOpnd::New(BuildSrcOpnd(AsmJsRegSlots::BufferReg, TyVar), constOffset, typeInfo.type, m_func);
15111487
indirOpnd->SetIndexOpnd(indexOpnd);
15121488
indirOpnd->GetBaseOpnd()->SetValueType(typeInfo.arrayType);
@@ -6915,36 +6891,12 @@ IRBuilderAsmJs::BuildAsmSimdTypedArr(Js::OpCodeAsmJs newOpcode, uint32 offset, u
69156891

69166892
switch (viewType)
69176893
{
6918-
case Js::ArrayBufferView::TYPE_INT8:
6919-
arrayType = ValueType::GetObject(ObjectType::Int8Array);
6920-
break;
6921-
case Js::ArrayBufferView::TYPE_UINT8:
6922-
arrayType = ValueType::GetObject(ObjectType::Uint8Array);
6923-
break;
6924-
case Js::ArrayBufferView::TYPE_INT16:
6925-
arrayType = ValueType::GetObject(ObjectType::Int16Array);
6926-
mask = (uint32)~1;
6927-
break;
6928-
case Js::ArrayBufferView::TYPE_UINT16:
6929-
arrayType = ValueType::GetObject(ObjectType::Uint16Array);
6930-
mask = (uint32)~1;
6931-
break;
6932-
case Js::ArrayBufferView::TYPE_INT32:
6933-
arrayType = ValueType::GetObject(ObjectType::Int32Array);
6934-
mask = (uint32)~3;
6935-
break;
6936-
case Js::ArrayBufferView::TYPE_UINT32:
6937-
arrayType = ValueType::GetObject(ObjectType::Uint32Array);
6938-
mask = (uint32)~3;
6939-
break;
6940-
case Js::ArrayBufferView::TYPE_FLOAT32:
6941-
arrayType = ValueType::GetObject(ObjectType::Float32Array);
6942-
mask = (uint32)~3;
6943-
break;
6944-
case Js::ArrayBufferView::TYPE_FLOAT64:
6945-
arrayType = ValueType::GetObject(ObjectType::Float64Array);
6946-
mask = (uint32)~7;
6894+
#define ARRAYBUFFER_VIEW(name, align, RegType, MemType, irSuffix) \
6895+
case Js::ArrayBufferView::TYPE_##name: \
6896+
mask = ARRAYBUFFER_VIEW_MASK(align); \
6897+
arrayType = ValueType::GetObject(ObjectType::##irSuffix##Array); \
69476898
break;
6899+
#include "Language/AsmJsArrayBufferViews.h"
69486900
default:
69496901
Assert(UNREACHED);
69506902
}

lib/Backend/JnHelperMethod.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ DECLSPEC_GUARDIGNORE _NOINLINE intptr_t GetNonTableMethodAddress(ThreadContextI
176176

177177
case HelperDirectMath_Tan:
178178
return ShiftAddr(context, (double(*)(double))__libm_sse2_tan);
179+
180+
case HelperAtomicStore64:
181+
return ShiftAddr(context, (double(*)(double))InterlockedExchange64);
182+
183+
case HelperMemoryBarrier:
184+
return ShiftAddr(context, (void(*)())MemoryBarrier);
179185
#endif
180186

181187
case HelperDirectMath_FloorDb:

lib/Backend/JnHelperMethodList.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,8 @@ HELPERCALL(DirectMath_Int64Rol , (int64(*)(int64,int64)) Wasm::WasmMath::Rol<int
589589
HELPERCALL(DirectMath_Int64Ror , (int64(*)(int64,int64)) Wasm::WasmMath::Ror<int64>, 0)
590590
HELPERCALL(DirectMath_Int64Clz , (int64(*)(int64)) Wasm::WasmMath::Clz<int64>, 0)
591591
HELPERCALL(DirectMath_Int64Ctz , (int64(*)(int64)) Wasm::WasmMath::Ctz<int64>, 0)
592+
HELPERCALL(AtomicStore64, nullptr, 0)
593+
HELPERCALL(MemoryBarrier, nullptr, 0)
592594
#elif defined(_M_X64)
593595
// AMD64 regular CRT calls -- on AMD64 calling convention is already what we want -- args in XMM0, XMM1 rather than on stack which is slower.
594596
HELPERCALL(DirectMath_Acos, nullptr, 0)

lib/Backend/Lower.cpp

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,9 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
11281128
case Js::OpCode::TrapIfZero:
11291129
LowerTrapIfZero(instr);
11301130
break;
1131+
case Js::OpCode::TrapIfUnalignedAccess:
1132+
instrPrev = LowerTrapIfUnalignedAccess(instr);
1133+
break;
11311134
case Js::OpCode::DivU_I4:
11321135
case Js::OpCode::Div_I4:
11331136
this->LowerDivI4(instr);
@@ -1541,10 +1544,18 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
15411544
instrPrev = LowerLdArrViewElem(instr);
15421545
break;
15431546

1547+
case Js::OpCode::StAtomicWasm:
1548+
instrPrev = LowerStAtomicsWasm(instr);
1549+
break;
1550+
15441551
case Js::OpCode::StArrViewElem:
15451552
instrPrev = LowerStArrViewElem(instr);
15461553
break;
15471554

1555+
case Js::OpCode::LdAtomicWasm:
1556+
instrPrev = LowerLdAtomicsWasm(instr);
1557+
break;
1558+
15481559
case Js::OpCode::LdArrViewElemWasm:
15491560
instrPrev = LowerLdArrViewElemWasm(instr);
15501561
break;
@@ -8943,7 +8954,7 @@ Lowerer::LowerLdArrViewElem(IR::Instr * instr)
89438954
}
89448955

89458956
IR::Instr *
8946-
Lowerer::LowerWasmMemOp(IR::Instr * instr, IR::Opnd *addrOpnd)
8957+
Lowerer::LowerWasmArrayBoundsCheck(IR::Instr * instr, IR::Opnd *addrOpnd)
89478958
{
89488959
uint32 offset = addrOpnd->AsIndirOpnd()->GetOffset();
89498960

@@ -8959,7 +8970,7 @@ Lowerer::LowerWasmMemOp(IR::Instr * instr, IR::Opnd *addrOpnd)
89598970
}
89608971
else
89618972
{
8962-
return m_lowererMD.LowerWasmMemOp(instr, addrOpnd);
8973+
return m_lowererMD.LowerWasmArrayBoundsCheck(instr, addrOpnd);
89638974
}
89648975
}
89658976

@@ -8979,7 +8990,7 @@ Lowerer::LowerLdArrViewElemWasm(IR::Instr * instr)
89798990
Assert(!dst->IsFloat32() || src1->IsFloat32());
89808991
Assert(!dst->IsFloat64() || src1->IsFloat64());
89818992

8982-
IR::Instr * done = LowerWasmMemOp(instr, src1);
8993+
IR::Instr * done = LowerWasmArrayBoundsCheck(instr, src1);
89838994
IR::Instr* newMove = InsertMove(dst, src1, done);
89848995

89858996
#if ENABLE_FAST_ARRAYBUFFER
@@ -8989,6 +9000,7 @@ Lowerer::LowerLdArrViewElemWasm(IR::Instr * instr)
89899000
#else
89909001
Unused(newMove);
89919002
#endif
9003+
89929004
instr->Remove();
89939005
return instrPrev;
89949006
#else
@@ -9150,6 +9162,57 @@ Lowerer::LowerMemOp(IR::Instr * instr)
91509162
return instrPrev;
91519163
}
91529164

9165+
IR::Instr*
9166+
Lowerer::LowerStAtomicsWasm(IR::Instr* instr)
9167+
{
9168+
#ifdef ENABLE_WASM
9169+
Assert(m_func->GetJITFunctionBody()->IsWasmFunction());
9170+
Assert(instr);
9171+
Assert(instr->m_opcode == Js::OpCode::StAtomicWasm);
9172+
9173+
IR::Instr * instrPrev = instr->m_prev;
9174+
9175+
IR::Opnd * dst = instr->GetDst();
9176+
IR::Opnd * src1 = instr->GetSrc1();
9177+
9178+
Assert(IRType_IsNativeInt(dst->GetType()));
9179+
9180+
IR::Instr * done = LowerWasmArrayBoundsCheck(instr, dst);
9181+
m_lowererMD.LowerAtomicStore(dst, src1, done);
9182+
9183+
instr->Remove();
9184+
return instrPrev;
9185+
#else
9186+
Assert(UNREACHED);
9187+
return instr;
9188+
#endif
9189+
}
9190+
9191+
IR::Instr * Lowerer::LowerLdAtomicsWasm(IR::Instr * instr)
9192+
{
9193+
#ifdef ENABLE_WASM
9194+
Assert(m_func->GetJITFunctionBody()->IsWasmFunction());
9195+
Assert(instr);
9196+
Assert(instr->m_opcode == Js::OpCode::LdAtomicWasm);
9197+
9198+
IR::Instr * instrPrev = instr->m_prev;
9199+
9200+
IR::Opnd * dst = instr->GetDst();
9201+
IR::Opnd * src1 = instr->GetSrc1();
9202+
9203+
Assert(IRType_IsNativeInt(dst->GetType()));
9204+
9205+
IR::Instr * done = LowerWasmArrayBoundsCheck(instr, src1);
9206+
m_lowererMD.LowerAtomicLoad(dst, src1, done);
9207+
9208+
instr->Remove();
9209+
return instrPrev;
9210+
#else
9211+
Assert(UNREACHED);
9212+
return instr;
9213+
#endif
9214+
}
9215+
91539216
IR::Instr *
91549217
Lowerer::LowerStArrViewElem(IR::Instr * instr)
91559218
{
@@ -9176,7 +9239,7 @@ Lowerer::LowerStArrViewElem(IR::Instr * instr)
91769239

91779240
if (m_func->GetJITFunctionBody()->IsWasmFunction())
91789241
{
9179-
done = LowerWasmMemOp(instr, dst);
9242+
done = LowerWasmArrayBoundsCheck(instr, dst);
91809243
}
91819244
else if (offset < 0)
91829245
{
@@ -24683,6 +24746,38 @@ Lowerer::LowerTrapIfZero(IR::Instr * const instr)
2468324746
LowererMD::ChangeToAssign(instr);
2468424747
}
2468524748

24749+
IR::Instr*
24750+
Lowerer::LowerTrapIfUnalignedAccess(IR::Instr * const instr)
24751+
{
24752+
IR::Opnd* src1 = instr->GetSrc1();
24753+
IR::Opnd* src2 = instr->UnlinkSrc2();
24754+
Assert(instr);
24755+
Assert(instr->m_opcode == Js::OpCode::TrapIfUnalignedAccess);
24756+
Assert(src1 && !src1->IsVar());
24757+
Assert(src2 && src2->IsImmediateOpnd());
24758+
Assert(src2->GetSize() > 1);
24759+
24760+
uint32 mask = src2->GetSize() - 1;
24761+
uint32 cmpValue = (uint32)src2->GetImmediateValue(m_func);
24762+
src2->Free(m_func);
24763+
24764+
IR::IntConstOpnd* maskOpnd = IR::IntConstOpnd::New(mask, src1->GetType(), m_func);
24765+
IR::RegOpnd* maskedOpnd = IR::RegOpnd::New(src1->GetType(), m_func);
24766+
IR::Instr* newInstr = IR::Instr::New(Js::OpCode::And_I4, maskedOpnd, src1, maskOpnd, m_func);
24767+
instr->InsertBefore(newInstr);
24768+
24769+
IR::IntConstOpnd* cmpOpnd = IR::IntConstOpnd::New(cmpValue, maskedOpnd->GetType(), m_func, true);
24770+
IR::LabelInstr* alignedLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
24771+
newInstr = IR::BranchInstr::New(Js::OpCode::BrEq_I4, alignedLabel, maskedOpnd, cmpOpnd, m_func);
24772+
instr->InsertBefore(newInstr);
24773+
InsertLabel(true, instr);
24774+
GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_UnalignedAtomicAccess), TyInt32, m_func), instr);
24775+
instr->InsertBefore(alignedLabel);
24776+
24777+
instr->m_opcode = Js::OpCode::Ld_I4;
24778+
return instr;
24779+
}
24780+
2468624781
void
2468724782
Lowerer::LowerTrapIfMinIntOverNegOne(IR::Instr * const instr)
2468824783
{

0 commit comments

Comments
 (0)