Implement atomicSub intrinsic

Polyfill this for HLSL using an atomic add with the operand negated.

Fixed: tint:1130
Change-Id: Ifa32d58973f1b48593ec0f6320f47f4358a5a3a9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/62760
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price
2021-08-26 15:26:25 +00:00
committed by Tint LUCI CQ
parent a96dce9c89
commit f9d19719fd
31 changed files with 1638 additions and 947 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -537,6 +537,7 @@ fn textureLoad(texture: texture_external, coords: vec2<i32>) -> vec4<f32>
[[stage("fragment", "compute")]] fn atomicLoad<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>) -> T
[[stage("fragment", "compute")]] fn atomicStore<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T)
[[stage("fragment", "compute")]] fn atomicAdd<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T
[[stage("fragment", "compute")]] fn atomicSub<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T
[[stage("fragment", "compute")]] fn atomicMax<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T
[[stage("fragment", "compute")]] fn atomicMin<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T
[[stage("fragment", "compute")]] fn atomicAnd<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T

View File

@@ -91,6 +91,7 @@ bool IsAtomicIntrinsic(IntrinsicType i) {
return i == sem::IntrinsicType::kAtomicLoad ||
i == sem::IntrinsicType::kAtomicStore ||
i == sem::IntrinsicType::kAtomicAdd ||
i == sem::IntrinsicType::kAtomicSub ||
i == sem::IntrinsicType::kAtomicMax ||
i == sem::IntrinsicType::kAtomicMin ||
i == sem::IntrinsicType::kAtomicAnd ||

View File

@@ -303,6 +303,9 @@ IntrinsicType ParseIntrinsicType(const std::string& name) {
if (name == "atomicAdd") {
return IntrinsicType::kAtomicAdd;
}
if (name == "atomicSub") {
return IntrinsicType::kAtomicSub;
}
if (name == "atomicMax") {
return IntrinsicType::kAtomicMax;
}
@@ -513,6 +516,8 @@ const char* str(IntrinsicType i) {
return "atomicStore";
case IntrinsicType::kAtomicAdd:
return "atomicAdd";
case IntrinsicType::kAtomicSub:
return "atomicSub";
case IntrinsicType::kAtomicMax:
return "atomicMax";
case IntrinsicType::kAtomicMin:

View File

@@ -125,6 +125,7 @@ enum class IntrinsicType {
kAtomicLoad,
kAtomicStore,
kAtomicAdd,
kAtomicSub,
kAtomicMax,
kAtomicMin,
kAtomicAnd,

View File

@@ -238,6 +238,9 @@ DecomposeMemoryAccess::Intrinsic* IntrinsicAtomicFor(ProgramBuilder* builder,
case sem::IntrinsicType::kAtomicAdd:
op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicAdd;
break;
case sem::IntrinsicType::kAtomicSub:
op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicSub;
break;
case sem::IntrinsicType::kAtomicMax:
op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicMax;
break;
@@ -723,6 +726,9 @@ std::string DecomposeMemoryAccess::Intrinsic::InternalName() const {
case Op::kAtomicAdd:
ss << "intrinsic_atomic_add_";
break;
case Op::kAtomicSub:
ss << "intrinsic_atomic_sub_";
break;
case Op::kAtomicMax:
ss << "intrinsic_atomic_max_";
break;

View File

@@ -46,6 +46,7 @@ class DecomposeMemoryAccess
kAtomicLoad,
kAtomicStore,
kAtomicAdd,
kAtomicSub,
kAtomicMax,
kAtomicMin,
kAtomicAnd,

View File

@@ -1237,6 +1237,7 @@ fn main() {
atomicStore(&sb.a, 123);
ignore(atomicLoad(&sb.a));
ignore(atomicAdd(&sb.a, 123));
ignore(atomicSub(&sb.a, 123));
ignore(atomicMax(&sb.a, 123));
ignore(atomicMin(&sb.a, 123));
ignore(atomicAnd(&sb.a, 123));
@@ -1248,6 +1249,7 @@ fn main() {
atomicStore(&sb.b, 123u);
ignore(atomicLoad(&sb.b));
ignore(atomicAdd(&sb.b, 123u));
ignore(atomicSub(&sb.b, 123u));
ignore(atomicMax(&sb.b, 123u));
ignore(atomicMin(&sb.b, 123u));
ignore(atomicAnd(&sb.b, 123u));
@@ -1277,56 +1279,62 @@ fn tint_symbol_1([[internal(disable_validation__ignore_constructible_function_pa
[[internal(intrinsic_atomic_add_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_2([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_max_storage_i32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_sub_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_3([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_min_storage_i32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_max_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_4([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_and_storage_i32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_min_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_5([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_or_storage_i32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_and_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_6([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_xor_storage_i32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_or_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_7([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_exchange_storage_i32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_xor_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_8([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_exchange_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_9([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32) -> i32
[[internal(intrinsic_atomic_compare_exchange_weak_storage_i32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_9([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32, param_2 : i32) -> vec2<i32>
fn tint_symbol_10([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : i32, param_2 : i32) -> vec2<i32>
[[internal(intrinsic_atomic_store_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_10([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32)
fn tint_symbol_11([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32)
[[internal(intrinsic_atomic_load_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_11([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32) -> u32
fn tint_symbol_12([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32) -> u32
[[internal(intrinsic_atomic_add_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_12([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_max_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_13([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_min_storage_u32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_sub_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_14([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_and_storage_u32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_max_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_15([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_or_storage_u32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_min_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_16([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_xor_storage_u32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_and_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_17([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_exchange_storage_u32), internal(disable_validation__function_has_no_body)]]
[[internal(intrinsic_atomic_or_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_18([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_xor_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_19([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_exchange_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_20([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32) -> u32
[[internal(intrinsic_atomic_compare_exchange_weak_storage_u32), internal(disable_validation__function_has_no_body)]]
fn tint_symbol_19([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32, param_2 : u32) -> vec2<u32>
fn tint_symbol_21([[internal(disable_validation__ignore_constructible_function_parameter)]] buffer : SB, offset : u32, param_1 : u32, param_2 : u32) -> vec2<u32>
[[stage(compute), workgroup_size(1)]]
fn main() {
@@ -1339,17 +1347,19 @@ fn main() {
ignore(tint_symbol_6(sb, 16u, 123));
ignore(tint_symbol_7(sb, 16u, 123));
ignore(tint_symbol_8(sb, 16u, 123));
ignore(tint_symbol_9(sb, 16u, 123, 345));
tint_symbol_10(sb, 20u, 123u);
ignore(tint_symbol_11(sb, 20u));
ignore(tint_symbol_12(sb, 20u, 123u));
ignore(tint_symbol_9(sb, 16u, 123));
ignore(tint_symbol_10(sb, 16u, 123, 345));
tint_symbol_11(sb, 20u, 123u);
ignore(tint_symbol_12(sb, 20u));
ignore(tint_symbol_13(sb, 20u, 123u));
ignore(tint_symbol_14(sb, 20u, 123u));
ignore(tint_symbol_15(sb, 20u, 123u));
ignore(tint_symbol_16(sb, 20u, 123u));
ignore(tint_symbol_17(sb, 20u, 123u));
ignore(tint_symbol_18(sb, 20u, 123u));
ignore(tint_symbol_19(sb, 20u, 123u, 345u));
ignore(tint_symbol_19(sb, 20u, 123u));
ignore(tint_symbol_20(sb, 20u, 123u));
ignore(tint_symbol_21(sb, 20u, 123u, 345u));
}
)";
@@ -1373,6 +1383,7 @@ fn main() {
atomicStore(&(w.a), 123);
ignore(atomicLoad(&(w.a)));
ignore(atomicAdd(&(w.a), 123));
ignore(atomicSub(&(w.a), 123));
ignore(atomicMax(&(w.a), 123));
ignore(atomicMin(&(w.a), 123));
ignore(atomicAnd(&(w.a), 123));
@@ -1383,6 +1394,7 @@ fn main() {
atomicStore(&(w.b), 123u);
ignore(atomicLoad(&(w.b)));
ignore(atomicAdd(&(w.b), 123u));
ignore(atomicSub(&(w.b), 123u));
ignore(atomicMax(&(w.b), 123u));
ignore(atomicMin(&(w.b), 123u));
ignore(atomicAnd(&(w.b), 123u));

View File

@@ -869,6 +869,7 @@ bool GeneratorImpl::EmitStorageBufferAccess(
case Op::kAtomicLoad:
case Op::kAtomicStore:
case Op::kAtomicAdd:
case Op::kAtomicSub:
case Op::kAtomicMax:
case Op::kAtomicMin:
case Op::kAtomicAnd:
@@ -930,7 +931,14 @@ bool GeneratorImpl::EmitStorageAtomicCall(
}
l << " = 0;";
}
line(&buf) << "buffer." << hlsl << "(offset, value, original_value);";
{
auto l = line(&buf);
l << "buffer." << hlsl << "(offset, ";
if (intrinsic->op == Op::kAtomicSub) {
l << "-";
}
l << "value, original_value);";
}
line(&buf) << "return original_value;";
return name;
};
@@ -939,6 +947,10 @@ bool GeneratorImpl::EmitStorageAtomicCall(
case Op::kAtomicAdd:
return rmw("atomicAdd", "InterlockedAdd");
case Op::kAtomicSub:
// Use add with the operand negated.
return rmw("atomicSub", "InterlockedAdd");
case Op::kAtomicMax:
return rmw("atomicMax", "InterlockedMax");
@@ -1130,6 +1142,10 @@ bool GeneratorImpl::EmitWorkgroupAtomicCall(std::ostream& out,
if (i > 0) {
pre << ", ";
}
if (i == 1 && intrinsic->Type() == sem::IntrinsicType::kAtomicSub) {
// Sub uses InterlockedAdd with the operand negated.
pre << "-";
}
if (!EmitExpression(pre, arg)) {
return false;
}
@@ -1240,6 +1256,7 @@ bool GeneratorImpl::EmitWorkgroupAtomicCall(std::ostream& out,
}
case sem::IntrinsicType::kAtomicAdd:
case sem::IntrinsicType::kAtomicSub:
return call("InterlockedAdd");
case sem::IntrinsicType::kAtomicMax:

View File

@@ -634,6 +634,9 @@ bool GeneratorImpl::EmitAtomicCall(std::ostream& out,
case sem::IntrinsicType::kAtomicAdd:
return call("atomic_fetch_add_explicit", true);
case sem::IntrinsicType::kAtomicSub:
return call("atomic_fetch_sub_explicit", true);
case sem::IntrinsicType::kAtomicMax:
return call("atomic_fetch_max_explicit", true);

View File

@@ -3151,6 +3151,15 @@ bool Builder::GenerateAtomicIntrinsic(ast::CallExpression* call,
semantics,
value,
});
case sem::IntrinsicType::kAtomicSub:
return push_function_inst(spv::Op::OpAtomicISub, {
result_type,
result_id,
pointer,
memory,
semantics,
value,
});
case sem::IntrinsicType::kAtomicMax:
return push_function_inst(
is_value_signed() ? spv::Op::OpAtomicSMax : spv::Op::OpAtomicUMax,