Skip to content

Commit 458c86a

Browse files
committed
[MERGE #4470 @Cellule] WASM: Atomic load/store
Merge pull request #4470 from Cellule:wasm/atomics Implement wasm atomic load/store operations.
2 parents 5bb7369 + 07b0be8 commit 458c86a

Some content is hidden

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

68 files changed

+1401
-531
lines changed

lib/Backend/GlobOpt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15650,6 +15650,7 @@ GlobOpt::PreLowerCanonicalize(IR::Instr *instr, Value **pSrc1Val, Value **pSrc2V
1565015650
case Js::OpCode::TrapIfMinIntOverNegOne:
1565115651
case Js::OpCode::TrapIfTruncOverflow:
1565215652
case Js::OpCode::TrapIfZero:
15653+
case Js::OpCode::TrapIfUnalignedAccess:
1565315654
case Js::OpCode::FromVar:
1565415655
case Js::OpCode::Conv_Prim:
1565515656
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
@@ -1420,64 +1420,20 @@ IRBuilderAsmJs::BuildStartCall(Js::OpCodeAsmJs newOpcode, uint32 offset)
14201420
void
14211421
IRBuilderAsmJs::InitializeMemAccessTypeInfo(Js::ArrayBufferView::ViewType viewType, _Out_ MemAccessTypeInfo * typeInfo)
14221422
{
1423-
typeInfo->type = TyInt32;
1424-
typeInfo->valueRegType = WAsmJs::INT32;
1425-
1423+
AssertOrFailFast(typeInfo);
1424+
14261425
switch (viewType)
14271426
{
1428-
case Js::ArrayBufferView::TYPE_INT8_TO_INT64:
1429-
typeInfo->valueRegType = WAsmJs::INT64;
1430-
case Js::ArrayBufferView::TYPE_INT8:
1431-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int8Array);
1432-
typeInfo->type = TyInt8;
1433-
break;
1434-
case Js::ArrayBufferView::TYPE_UINT8_TO_INT64:
1435-
typeInfo->valueRegType = WAsmJs::INT64;
1436-
case Js::ArrayBufferView::TYPE_UINT8:
1437-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Uint8Array);
1438-
typeInfo->type = TyUint8;
1439-
break;
1440-
case Js::ArrayBufferView::TYPE_INT16_TO_INT64:
1441-
typeInfo->valueRegType = WAsmJs::INT64;
1442-
case Js::ArrayBufferView::TYPE_INT16:
1443-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int16Array);
1444-
typeInfo->type = TyInt16;
1445-
break;
1446-
case Js::ArrayBufferView::TYPE_UINT16_TO_INT64:
1447-
typeInfo->valueRegType = WAsmJs::INT64;
1448-
case Js::ArrayBufferView::TYPE_UINT16:
1449-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Uint16Array);
1450-
typeInfo->type = TyUint16;
1451-
break;
1452-
case Js::ArrayBufferView::TYPE_INT32_TO_INT64:
1453-
typeInfo->valueRegType = WAsmJs::INT64;
1454-
case Js::ArrayBufferView::TYPE_INT32:
1455-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int32Array);
1456-
typeInfo->type = TyInt32;
1457-
break;
1458-
case Js::ArrayBufferView::TYPE_UINT32_TO_INT64:
1459-
typeInfo->valueRegType = WAsmJs::INT64;
1460-
case Js::ArrayBufferView::TYPE_UINT32:
1461-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Uint32Array);
1462-
typeInfo->type = TyUint32;
1463-
break;
1464-
case Js::ArrayBufferView::TYPE_FLOAT32:
1465-
typeInfo->valueRegType = WAsmJs::FLOAT32;
1466-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Float32Array);
1467-
typeInfo->type = TyFloat32;
1468-
break;
1469-
case Js::ArrayBufferView::TYPE_FLOAT64:
1470-
typeInfo->valueRegType = WAsmJs::FLOAT64;
1471-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Float64Array);
1472-
typeInfo->type = TyFloat64;
1473-
break;
1474-
case Js::ArrayBufferView::TYPE_INT64:
1475-
typeInfo->valueRegType = WAsmJs::INT64;
1476-
typeInfo->arrayType = ValueType::GetObject(ObjectType::Int64Array);
1477-
typeInfo->type = TyInt64;
1427+
#define ARRAYBUFFER_VIEW(name, align, RegType, MemType, irSuffix) \
1428+
case Js::ArrayBufferView::TYPE_##name: \
1429+
typeInfo->valueRegType = WAsmJs::FromPrimitiveType<RegType>(); \
1430+
typeInfo->type = Ty##irSuffix;\
1431+
typeInfo->arrayType = ValueType::GetObject(ObjectType::##irSuffix##Array); \
1432+
Assert(TySize[Ty##irSuffix] == (1<<align)); \
14781433
break;
1434+
#include "Language/AsmJsArrayBufferViews.h"
14791435
default:
1480-
Assume(UNREACHED);
1436+
AssertOrFailFast(UNREACHED);
14811437
}
14821438
}
14831439

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

14991458
MemAccessTypeInfo typeInfo;
15001459
InitializeMemAccessTypeInfo(viewType, &typeInfo);
1460+
const uint32 memAccessSize = TySize[typeInfo.type];
15011461

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

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

68786854
switch (viewType)
68796855
{
6880-
case Js::ArrayBufferView::TYPE_INT8:
6881-
arrayType = ValueType::GetObject(ObjectType::Int8Array);
6882-
break;
6883-
case Js::ArrayBufferView::TYPE_UINT8:
6884-
arrayType = ValueType::GetObject(ObjectType::Uint8Array);
6885-
break;
6886-
case Js::ArrayBufferView::TYPE_INT16:
6887-
arrayType = ValueType::GetObject(ObjectType::Int16Array);
6888-
mask = (uint32)~1;
6889-
break;
6890-
case Js::ArrayBufferView::TYPE_UINT16:
6891-
arrayType = ValueType::GetObject(ObjectType::Uint16Array);
6892-
mask = (uint32)~1;
6893-
break;
6894-
case Js::ArrayBufferView::TYPE_INT32:
6895-
arrayType = ValueType::GetObject(ObjectType::Int32Array);
6896-
mask = (uint32)~3;
6897-
break;
6898-
case Js::ArrayBufferView::TYPE_UINT32:
6899-
arrayType = ValueType::GetObject(ObjectType::Uint32Array);
6900-
mask = (uint32)~3;
6901-
break;
6902-
case Js::ArrayBufferView::TYPE_FLOAT32:
6903-
arrayType = ValueType::GetObject(ObjectType::Float32Array);
6904-
mask = (uint32)~3;
6905-
break;
6906-
case Js::ArrayBufferView::TYPE_FLOAT64:
6907-
arrayType = ValueType::GetObject(ObjectType::Float64Array);
6908-
mask = (uint32)~7;
6856+
#define ARRAYBUFFER_VIEW(name, align, RegType, MemType, irSuffix) \
6857+
case Js::ArrayBufferView::TYPE_##name: \
6858+
mask = ARRAYBUFFER_VIEW_MASK(align); \
6859+
arrayType = ValueType::GetObject(ObjectType::##irSuffix##Array); \
69096860
break;
6861+
#include "Language/AsmJsArrayBufferViews.h"
69106862
default:
69116863
Assert(UNREACHED);
69126864
}

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
@@ -582,6 +582,8 @@ HELPERCALL(DirectMath_Int64Rol , (int64(*)(int64,int64)) Wasm::WasmMath::Rol<int
582582
HELPERCALL(DirectMath_Int64Ror , (int64(*)(int64,int64)) Wasm::WasmMath::Ror<int64>, 0)
583583
HELPERCALL(DirectMath_Int64Clz , (int64(*)(int64)) Wasm::WasmMath::Clz<int64>, 0)
584584
HELPERCALL(DirectMath_Int64Ctz , (int64(*)(int64)) Wasm::WasmMath::Ctz<int64>, 0)
585+
HELPERCALL(AtomicStore64, nullptr, 0)
586+
HELPERCALL(MemoryBarrier, nullptr, 0)
585587
#elif defined(_M_X64)
586588
// AMD64 regular CRT calls -- on AMD64 calling convention is already what we want -- args in XMM0, XMM1 rather than on stack which is slower.
587589
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;
@@ -9172,7 +9183,7 @@ Lowerer::LowerLdArrViewElem(IR::Instr * instr)
91729183
}
91739184

91749185
IR::Instr *
9175-
Lowerer::LowerWasmMemOp(IR::Instr * instr, IR::Opnd *addrOpnd)
9186+
Lowerer::LowerWasmArrayBoundsCheck(IR::Instr * instr, IR::Opnd *addrOpnd)
91769187
{
91779188
uint32 offset = addrOpnd->AsIndirOpnd()->GetOffset();
91789189

@@ -9188,7 +9199,7 @@ Lowerer::LowerWasmMemOp(IR::Instr * instr, IR::Opnd *addrOpnd)
91889199
}
91899200
else
91909201
{
9191-
return m_lowererMD.LowerWasmMemOp(instr, addrOpnd);
9202+
return m_lowererMD.LowerWasmArrayBoundsCheck(instr, addrOpnd);
91929203
}
91939204
}
91949205

@@ -9208,7 +9219,7 @@ Lowerer::LowerLdArrViewElemWasm(IR::Instr * instr)
92089219
Assert(!dst->IsFloat32() || src1->IsFloat32());
92099220
Assert(!dst->IsFloat64() || src1->IsFloat64());
92109221

9211-
IR::Instr * done = LowerWasmMemOp(instr, src1);
9222+
IR::Instr * done = LowerWasmArrayBoundsCheck(instr, src1);
92129223
IR::Instr* newMove = InsertMove(dst, src1, done);
92139224

92149225
#if ENABLE_FAST_ARRAYBUFFER
@@ -9218,6 +9229,7 @@ Lowerer::LowerLdArrViewElemWasm(IR::Instr * instr)
92189229
#else
92199230
Unused(newMove);
92209231
#endif
9232+
92219233
instr->Remove();
92229234
return instrPrev;
92239235
#else
@@ -9379,6 +9391,57 @@ Lowerer::LowerMemOp(IR::Instr * instr)
93799391
return instrPrev;
93809392
}
93819393

9394+
IR::Instr*
9395+
Lowerer::LowerStAtomicsWasm(IR::Instr* instr)
9396+
{
9397+
#ifdef ENABLE_WASM
9398+
Assert(m_func->GetJITFunctionBody()->IsWasmFunction());
9399+
Assert(instr);
9400+
Assert(instr->m_opcode == Js::OpCode::StAtomicWasm);
9401+
9402+
IR::Instr * instrPrev = instr->m_prev;
9403+
9404+
IR::Opnd * dst = instr->GetDst();
9405+
IR::Opnd * src1 = instr->GetSrc1();
9406+
9407+
Assert(IRType_IsNativeInt(dst->GetType()));
9408+
9409+
IR::Instr * done = LowerWasmArrayBoundsCheck(instr, dst);
9410+
m_lowererMD.LowerAtomicStore(dst, src1, done);
9411+
9412+
instr->Remove();
9413+
return instrPrev;
9414+
#else
9415+
Assert(UNREACHED);
9416+
return instr;
9417+
#endif
9418+
}
9419+
9420+
IR::Instr * Lowerer::LowerLdAtomicsWasm(IR::Instr * instr)
9421+
{
9422+
#ifdef ENABLE_WASM
9423+
Assert(m_func->GetJITFunctionBody()->IsWasmFunction());
9424+
Assert(instr);
9425+
Assert(instr->m_opcode == Js::OpCode::LdAtomicWasm);
9426+
9427+
IR::Instr * instrPrev = instr->m_prev;
9428+
9429+
IR::Opnd * dst = instr->GetDst();
9430+
IR::Opnd * src1 = instr->GetSrc1();
9431+
9432+
Assert(IRType_IsNativeInt(dst->GetType()));
9433+
9434+
IR::Instr * done = LowerWasmArrayBoundsCheck(instr, src1);
9435+
m_lowererMD.LowerAtomicLoad(dst, src1, done);
9436+
9437+
instr->Remove();
9438+
return instrPrev;
9439+
#else
9440+
Assert(UNREACHED);
9441+
return instr;
9442+
#endif
9443+
}
9444+
93829445
IR::Instr *
93839446
Lowerer::LowerStArrViewElem(IR::Instr * instr)
93849447
{
@@ -9405,7 +9468,7 @@ Lowerer::LowerStArrViewElem(IR::Instr * instr)
94059468

94069469
if (m_func->GetJITFunctionBody()->IsWasmFunction())
94079470
{
9408-
done = LowerWasmMemOp(instr, dst);
9471+
done = LowerWasmArrayBoundsCheck(instr, dst);
94099472
}
94109473
else if (offset < 0)
94119474
{
@@ -25386,6 +25449,38 @@ Lowerer::LowerTrapIfZero(IR::Instr * const instr)
2538625449
LowererMD::ChangeToAssign(instr);
2538725450
}
2538825451

25452+
IR::Instr*
25453+
Lowerer::LowerTrapIfUnalignedAccess(IR::Instr * const instr)
25454+
{
25455+
IR::Opnd* src1 = instr->GetSrc1();
25456+
IR::Opnd* src2 = instr->UnlinkSrc2();
25457+
Assert(instr);
25458+
Assert(instr->m_opcode == Js::OpCode::TrapIfUnalignedAccess);
25459+
Assert(src1 && !src1->IsVar());
25460+
Assert(src2 && src2->IsImmediateOpnd());
25461+
Assert(src2->GetSize() > 1);
25462+
25463+
uint32 mask = src2->GetSize() - 1;
25464+
uint32 cmpValue = (uint32)src2->GetImmediateValue(m_func);
25465+
src2->Free(m_func);
25466+
25467+
IR::IntConstOpnd* maskOpnd = IR::IntConstOpnd::New(mask, src1->GetType(), m_func);
25468+
IR::RegOpnd* maskedOpnd = IR::RegOpnd::New(src1->GetType(), m_func);
25469+
IR::Instr* newInstr = IR::Instr::New(Js::OpCode::And_I4, maskedOpnd, src1, maskOpnd, m_func);
25470+
instr->InsertBefore(newInstr);
25471+
25472+
IR::IntConstOpnd* cmpOpnd = IR::IntConstOpnd::New(cmpValue, maskedOpnd->GetType(), m_func, true);
25473+
IR::LabelInstr* alignedLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
25474+
newInstr = IR::BranchInstr::New(Js::OpCode::BrEq_I4, alignedLabel, maskedOpnd, cmpOpnd, m_func);
25475+
instr->InsertBefore(newInstr);
25476+
InsertLabel(true, instr);
25477+
GenerateThrow(IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_UnalignedAtomicAccess), TyInt32, m_func), instr);
25478+
instr->InsertBefore(alignedLabel);
25479+
25480+
instr->m_opcode = Js::OpCode::Ld_I4;
25481+
return instr;
25482+
}
25483+
2538925484
void
2539025485
Lowerer::LowerTrapIfMinIntOverNegOne(IR::Instr * const instr)
2539125486
{

0 commit comments

Comments
 (0)