Fix ExtraInfo on F32Add and similar.

We want to ignore the incoming pending NaN state (since the pending will propagate to the output if there was one on the input), and we want to add a new pending NaN state if we can (that is to say, if it isn't cancelled out by both inputs having arithmetic state). Do this by discarding the pending states on the inputs, intersecting them (to keep only the arithmetic state), then union in a pending nan state (which might do nothing, if it's arithmetic).

If the above sounds confusing, keep in mind that when a value is arithmetic, the act of performing the "NaN canonicalization" is a no-op. Thus, being arithmetic cancels out pending NaN states.
This commit is contained in:
Nick Lewycky
2019-11-06 23:15:49 -08:00
parent a06c858087
commit fa576093c2

View File

@ -2907,110 +2907,122 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
Operator::F32Add => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let i1 = i1 | ExtraInfo::pending_f32_nan();
let i2 = i2 | ExtraInfo::pending_f32_nan();
let res = builder.build_float_add(v1, v2, &state.var_name());
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(),
);
}
Operator::F64Add => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let i1 = i1 | ExtraInfo::pending_f64_nan();
let i2 = i2 | ExtraInfo::pending_f64_nan();
let res = builder.build_float_add(v1, v2, &state.var_name());
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(),
);
}
Operator::F32x4Add => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1);
let (v2, i2) = v128_into_f32x4(builder, intrinsics, v2, i2);
let i1 = i1 | ExtraInfo::pending_f32_nan();
let i2 = i2 | ExtraInfo::pending_f32_nan();
let res = builder.build_float_add(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(),
);
}
Operator::F64x2Add => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1);
let (v2, i2) = v128_into_f64x2(builder, intrinsics, v2, i2);
let i1 = i1 | ExtraInfo::pending_f64_nan();
let i2 = i2 | ExtraInfo::pending_f64_nan();
let res = builder.build_float_add(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(),
);
}
Operator::F32Sub => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let i1 = i1 | ExtraInfo::pending_f32_nan();
let i2 = i2 | ExtraInfo::pending_f32_nan();
let res = builder.build_float_sub(v1, v2, &state.var_name());
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(),
);
}
Operator::F64Sub => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let i1 = i1 | ExtraInfo::pending_f64_nan();
let i2 = i2 | ExtraInfo::pending_f64_nan();
let res = builder.build_float_sub(v1, v2, &state.var_name());
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(),
);
}
Operator::F32x4Sub => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1);
let (v2, i2) = v128_into_f32x4(builder, intrinsics, v2, i2);
let i1 = i1 | ExtraInfo::pending_f32_nan();
let i2 = i2 | ExtraInfo::pending_f32_nan();
let res = builder.build_float_sub(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(),
);
}
Operator::F64x2Sub => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1);
let (v2, i2) = v128_into_f64x2(builder, intrinsics, v2, i2);
let i1 = i1 | ExtraInfo::pending_f64_nan();
let i2 = i2 | ExtraInfo::pending_f64_nan();
let res = builder.build_float_sub(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(),
);
}
Operator::F32Mul => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let i1 = i1 | ExtraInfo::pending_f32_nan();
let i2 = i2 | ExtraInfo::pending_f32_nan();
let res = builder.build_float_mul(v1, v2, &state.var_name());
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(),
);
}
Operator::F64Mul => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let i1 = i1 | ExtraInfo::pending_f64_nan();
let i2 = i2 | ExtraInfo::pending_f64_nan();
let res = builder.build_float_mul(v1, v2, &state.var_name());
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(),
);
}
Operator::F32x4Mul => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, i1) = v128_into_f32x4(builder, intrinsics, v1, i1);
let (v2, i2) = v128_into_f32x4(builder, intrinsics, v2, i2);
let i1 = i1 | ExtraInfo::pending_f32_nan();
let i2 = i2 | ExtraInfo::pending_f32_nan();
let res = builder.build_float_mul(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f32_nan(),
);
}
Operator::F64x2Mul => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
let (v1, i1) = v128_into_f64x2(builder, intrinsics, v1, i1);
let (v2, i2) = v128_into_f64x2(builder, intrinsics, v2, i2);
let i1 = i1 | ExtraInfo::pending_f64_nan();
let i2 = i2 | ExtraInfo::pending_f64_nan();
let res = builder.build_float_mul(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, i1 & i2);
state.push1_extra(
res,
(i1.strip_pending() & i2.strip_pending()) | ExtraInfo::pending_f64_nan(),
);
}
Operator::F32Div => {
let (v1, v2) = state.pop2()?;