-
Notifications
You must be signed in to change notification settings - Fork 839
Add IR, parsing and binary support for AtomicRMW instructions from wasm threads proposal #1082
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4750f31
f79fed2
f19ccb2
1b58b17
5141e46
58b8727
654fafc
8520d80
f15707d
c004c55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -832,6 +832,51 @@ void WasmBinaryWriter::visitStore(Store *curr) { | |
| emitMemoryAccess(curr->align, curr->bytes, curr->offset); | ||
| } | ||
|
|
||
| void WasmBinaryWriter::visitAtomicRMW(AtomicRMW *curr) { | ||
| if (debug) std::cerr << "zz node: AtomicRMW" << std::endl; | ||
| recurse(curr->ptr); | ||
| recurse(curr->value); | ||
|
|
||
| o << int8_t(BinaryConsts::AtomicPrefix); | ||
|
|
||
| #define CASE_FOR_OP(Op) \ | ||
| case Op: \ | ||
| switch (curr->type) { \ | ||
| case i32: \ | ||
| switch (curr->bytes) { \ | ||
| case 1: o << int8_t(BinaryConsts::I32AtomicRMW##Op##8U); break; \ | ||
| case 2: o << int8_t(BinaryConsts::I32AtomicRMW##Op##16U); break; \ | ||
| case 4: o << int8_t(BinaryConsts::I32AtomicRMW##Op); break; \ | ||
| default: WASM_UNREACHABLE(); \ | ||
| } \ | ||
| break; \ | ||
| case i64: \ | ||
| switch (curr->bytes) { \ | ||
| case 1: o << int8_t(BinaryConsts::I64AtomicRMW##Op##8U); break; \ | ||
| case 2: o << int8_t(BinaryConsts::I64AtomicRMW##Op##16U); break; \ | ||
| case 4: o << int8_t(BinaryConsts::I64AtomicRMW##Op##32U); break; \ | ||
| case 8: o << int8_t(BinaryConsts::I64AtomicRMW##Op); break; \ | ||
| default: WASM_UNREACHABLE(); \ | ||
| } \ | ||
| break; \ | ||
| default: WASM_UNREACHABLE(); \ | ||
| } \ | ||
| break | ||
|
|
||
| switch(curr->op) { | ||
| CASE_FOR_OP(Add); | ||
| CASE_FOR_OP(Sub); | ||
| CASE_FOR_OP(And); | ||
| CASE_FOR_OP(Or); | ||
| CASE_FOR_OP(Xor); | ||
| CASE_FOR_OP(Xchg); | ||
| default: WASM_UNREACHABLE(); | ||
| } | ||
| #undef CASE_FOR_OP | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yay for big ugly macros that make the actual code easier to read. |
||
|
|
||
| emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); | ||
| } | ||
|
|
||
| void WasmBinaryWriter::visitConst(Const *curr) { | ||
| if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl; | ||
| switch (curr->type) { | ||
|
|
@@ -1934,6 +1979,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { | |
| code = getInt8(); | ||
| if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; | ||
| if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; | ||
| if (maybeVisitAtomicRMW(curr, code)) break; | ||
| throw ParseException("invalid code after atomic prefix: " + std::to_string(code)); | ||
| } | ||
| default: { | ||
|
|
@@ -2282,6 +2328,50 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA | |
| return true; | ||
| } | ||
|
|
||
|
|
||
| bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { | ||
| if (code < BinaryConsts::AtomicRMWOps_Begin || code > BinaryConsts::AtomicRMWOps_End) return false; | ||
| auto* curr = allocator.alloc<AtomicRMW>(); | ||
|
|
||
| // Set curr to the given opcode, type and size. | ||
| #define SET(opcode, optype, size) \ | ||
| curr->op = opcode; \ | ||
| curr->type = optype; \ | ||
| curr->bytes = size | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if I should complain that a missing terminal semicolon is a bit odd looking, considering that this is a nested macro that's right next to its use. I guess: at a quick skim, this gave me a double take, but this is almost-certainly OK.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, most function-like macros lack the semicolon so you can do
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I often add a comment when I do this in a macro, e.g.: Kind of like adding |
||
|
|
||
| // Handle the cases for all the valid types for a particular opcode | ||
| #define SET_FOR_OP(Op) \ | ||
| case BinaryConsts::I32AtomicRMW##Op: SET(Op, i32, 4); break; \ | ||
| case BinaryConsts::I32AtomicRMW##Op##8U: SET(Op, i32, 1); break; \ | ||
| case BinaryConsts::I32AtomicRMW##Op##16U: SET(Op, i32, 2); break; \ | ||
| case BinaryConsts::I64AtomicRMW##Op: SET(Op, i64, 8); break; \ | ||
| case BinaryConsts::I64AtomicRMW##Op##8U: SET(Op, i64, 1); break; \ | ||
| case BinaryConsts::I64AtomicRMW##Op##16U: SET(Op, i64, 2); break; \ | ||
| case BinaryConsts::I64AtomicRMW##Op##32U: SET(Op, i64, 4); break; | ||
|
|
||
| switch(code) { | ||
| SET_FOR_OP(Add); | ||
| SET_FOR_OP(Sub); | ||
| SET_FOR_OP(And); | ||
| SET_FOR_OP(Or); | ||
| SET_FOR_OP(Xor); | ||
| SET_FOR_OP(Xchg); | ||
| default: WASM_UNREACHABLE(); | ||
| } | ||
| #undef SET_FOR_OP | ||
| #undef SET | ||
|
|
||
| if (debug) std::cerr << "zz node: AtomicRMW" << std::endl; | ||
| Address readAlign; | ||
| readMemoryAccess(readAlign, curr->bytes, curr->offset); | ||
| if (readAlign != curr->bytes) throw ParseException("Align of AtomicRMW must match size"); | ||
| curr->value = popNonVoidExpression(); | ||
| curr->ptr = popNonVoidExpression(); | ||
| curr->finalize(); | ||
| out = curr; | ||
| return true; | ||
| } | ||
|
|
||
| bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) { | ||
| Const* curr; | ||
| if (debug) std::cerr << "zz node: Const, code " << code << std::endl; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with landing this PR with not all opt passes working yet, we can fix those later. Could we keep this line normal-looking, then, with
{}like the others? Or is the return here temporarily necessary?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type is necessary because without it, non-void-returning visitors won't compile unless they have their own override. Probably all of these templates should have the
return ReturnType()instead of being empty, otherwise passes can't fall back on them, which I'm guessing was not the intention.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, makes sense. Yeah, I think we should update them all to the
{ return ReturnType; }form