|
8 | 8 |
|
9 | 9 | // HACK(eddyb) newtypes to make it easy to tell apart Rust vs C++ specifics. |
10 | 10 | struct Rust<T>(T); |
11 | | -struct Cxx<T>(T); |
12 | 11 |
|
13 | 12 | use self::OpKind::*; |
14 | 13 | enum OpKind { |
15 | 14 | Unary(char), |
16 | 15 | Binary(char), |
17 | | - Ternary(Rust<&'static str>, Cxx<&'static str>), |
| 16 | + Ternary(Rust<&'static str>), |
18 | 17 |
|
19 | 18 | // HACK(eddyb) all other ops have floating-point inputs *and* outputs, so |
20 | 19 | // the easiest way to fuzz conversions from/to other types, even if it won't |
@@ -58,7 +57,7 @@ const OPS: &[(&str, OpKind)] = &[ |
58 | 57 | ("Div", Binary('/')), |
59 | 58 | ("Rem", Binary('%')), |
60 | 59 | // Ternary (`(F, F) -> F`) ops. |
61 | | - ("MulAdd", Ternary(Rust("mul_add"), Cxx("fusedMultiplyAdd"))), |
| 60 | + ("MulAdd", Ternary(Rust("mul_add"))), |
62 | 61 | // Roundtrip (`F -> T -> F`) ops. |
63 | 62 | ("FToI128ToF", Roundtrip(Type::SInt(128))), |
64 | 63 | ("FToU128ToF", Roundtrip(Type::UInt(128))), |
@@ -151,7 +150,7 @@ impl<HF> FuzzOp<HF> |
151 | 150 | let expr = match kind { |
152 | 151 | Unary(op) => format!("{op}{}", inputs[0]), |
153 | 152 | Binary(op) => format!("{} {op} {}", inputs[0], inputs[1]), |
154 | | - Ternary(Rust(method), _) => { |
| 153 | + Ternary(Rust(method)) => { |
155 | 154 | format!("{}.{method}({}, {})", inputs[0], inputs[1], inputs[2]) |
156 | 155 | } |
157 | 156 | Roundtrip(ty) => format!( |
@@ -186,7 +185,7 @@ impl<F> FuzzOp<F> |
186 | 185 | let expr = match kind { |
187 | 186 | Unary(op) => format!("{op}{}", inputs[0]), |
188 | 187 | Binary(op) => format!("({} {op} {}).value", inputs[0], inputs[1]), |
189 | | - Ternary(Rust(method), _) => { |
| 188 | + Ternary(Rust(method)) => { |
190 | 189 | format!("{}.{method}({}).value", inputs[0], inputs[1..].join(", ")) |
191 | 190 | } |
192 | 191 | Roundtrip(ty @ (Type::SInt(_) | Type::UInt(_))) => { |
@@ -224,159 +223,3 @@ impl<F> FuzzOp<F> |
224 | 223 | } |
225 | 224 | }" |
226 | 225 | } |
227 | | - |
228 | | -pub fn generate_cxx(exported_symbols: &mut Vec<String>) -> String { |
229 | | - String::new() |
230 | | - + r#" |
231 | | -#include <array> |
232 | | -#include <llvm/ADT/APFloat.h> |
233 | | -
|
234 | | -using namespace llvm; |
235 | | -
|
236 | | -#pragma clang diagnostic error "-Wall" |
237 | | -#pragma clang diagnostic error "-Wextra" |
238 | | -#pragma clang diagnostic error "-Wunknown-attributes" |
239 | | -
|
240 | | -using uint128_t = __uint128_t; |
241 | | -
|
242 | | -template<typename F> |
243 | | -struct FuzzOp { |
244 | | - enum : uint8_t {"# |
245 | | - + &all_ops_map_concat(|tag, name, _kind| { |
246 | | - format!( |
247 | | - " |
248 | | - {name} = {tag}," |
249 | | - ) |
250 | | - }) |
251 | | - + " |
252 | | - } tag; |
253 | | - F a, b, c; |
254 | | -
|
255 | | - F eval() const { |
256 | | -
|
257 | | - // HACK(eddyb) 'scratch' variables used by expressions below. |
258 | | - APFloat r(0.0); |
259 | | - APSInt i; |
260 | | - bool scratch_bool; |
261 | | -
|
262 | | - switch(tag) { |
263 | | - " |
264 | | - + &all_ops_map_concat(|_tag, name, kind| { |
265 | | - let inputs = kind.inputs(&["a.to_apf()", "b.to_apf()", "c.to_apf()"]); |
266 | | - let expr = match kind { |
267 | | - // HACK(eddyb) `APFloat` doesn't overload `operator%`, so we have |
268 | | - // to go through the `mod` method instead. |
269 | | - Binary('%') => format!("((r = {}), r.mod({}), r)", inputs[0], inputs[1]), |
270 | | - |
271 | | - Unary(op) => format!("{op}{}", inputs[0]), |
272 | | - Binary(op) => format!("{} {op} {}", inputs[0], inputs[1]), |
273 | | - |
274 | | - Ternary(_, Cxx(method)) => { |
275 | | - format!( |
276 | | - "((r = {}), r.{method}({}, {}, APFloat::rmNearestTiesToEven), r)", |
277 | | - inputs[0], inputs[1], inputs[2] |
278 | | - ) |
279 | | - } |
280 | | - |
281 | | - Roundtrip(ty @ (Type::SInt(_) | Type::UInt(_))) => { |
282 | | - let (w, signed) = match ty { |
283 | | - Type::SInt(w) => (w, true), |
284 | | - Type::UInt(w) => (w, false), |
285 | | - Type::Float(_) => unreachable!(), |
286 | | - }; |
287 | | - format!( |
288 | | - "((r = {}), |
289 | | - (i = APSInt({w}, !{signed})), |
290 | | - r.convertToInteger(i, APFloat::rmTowardZero, &scratch_bool), |
291 | | - r.convertFromAPInt(i, {signed}, APFloat::rmNearestTiesToEven), |
292 | | - r)", |
293 | | - inputs[0] |
294 | | - ) |
295 | | - } |
296 | | - Roundtrip(Type::Float(w)) => { |
297 | | - let cxx_apf_semantics = match w { |
298 | | - 32 => "APFloat::IEEEsingle()", |
299 | | - 64 => "APFloat::IEEEdouble()", |
300 | | - _ => unreachable!(), |
301 | | - }; |
302 | | - format!( |
303 | | - "((r = {input}), |
304 | | - r.convert({cxx_apf_semantics}, APFloat::rmNearestTiesToEven, &scratch_bool), |
305 | | - r.convert({input}.getSemantics(), APFloat::rmNearestTiesToEven, &scratch_bool), |
306 | | - r)", |
307 | | - input = inputs[0] |
308 | | - ) |
309 | | - } |
310 | | - }; |
311 | | - format!( |
312 | | - " |
313 | | - case {name}: return F::from_apf({expr});" |
314 | | - ) |
315 | | - }) |
316 | | - + " |
317 | | - } |
318 | | - } |
319 | | -}; |
320 | | -" + &[ |
321 | | - (16, "IEEEhalf"), |
322 | | - (32, "IEEEsingle"), |
323 | | - (64, "IEEEdouble"), |
324 | | - (128, "IEEEquad"), |
325 | | - (16, "BFloat"), |
326 | | - (8, "Float8E5M2"), |
327 | | - (8, "Float8E4M3FN"), |
328 | | - (80, "x87DoubleExtended"), |
329 | | - ] |
330 | | - .into_iter() |
331 | | - .map(|(w, cxx_apf_semantics): (usize, _)| { |
332 | | - let uint_width = w.next_power_of_two(); |
333 | | - let name = match (w, cxx_apf_semantics) { |
334 | | - (16, "BFloat") => "BrainF16".into(), |
335 | | - (8, s) if s.starts_with("Float8") => s.replace("Float8", "F8"), |
336 | | - (80, "x87DoubleExtended") => "X87_F80".into(), |
337 | | - _ => { |
338 | | - assert!(cxx_apf_semantics.starts_with("IEEE")); |
339 | | - format!("IEEE{w}") |
340 | | - } |
341 | | - }; |
342 | | - let exported_symbol = format!("cxx_apf_fuzz_eval_op_{}", name.to_ascii_lowercase()); |
343 | | - exported_symbols.push(exported_symbol.clone()); |
344 | | - let uint = format!("uint{uint_width}_t"); |
345 | | - format!( |
346 | | - r#" |
347 | | -struct __attribute__((packed)) {name} {{ |
348 | | - {uint} bits; |
349 | | -
|
350 | | - // HACK(eddyb) these work around `APInt` only being convenient up to 64 bits. |
351 | | -
|
352 | | - static {name} from_apf(APFloat apf) {{ |
353 | | - auto ap_bits = apf.bitcastToAPInt(); |
354 | | - assert(ap_bits.getBitWidth() == {w}); |
355 | | -
|
356 | | - {uint} bits = 0; |
357 | | - for(int i = 0; i < {w}; i += APInt::APINT_BITS_PER_WORD) |
358 | | - bits |= static_cast<{uint}>( |
359 | | - ap_bits.getRawData()[i / APInt::APINT_BITS_PER_WORD] |
360 | | - ) << i; |
361 | | - return {{ bits }}; |
362 | | - }} |
363 | | -
|
364 | | - APFloat to_apf() const {{ |
365 | | - std::array< |
366 | | - APInt::WordType, |
367 | | - ({w} + APInt::APINT_BITS_PER_WORD - 1) / APInt::APINT_BITS_PER_WORD |
368 | | - > words; |
369 | | - for(int i = 0; i < {w}; i += APInt::APINT_BITS_PER_WORD) |
370 | | - words[i / APInt::APINT_BITS_PER_WORD] = bits >> i; |
371 | | - return APFloat(APFloat::{cxx_apf_semantics}(), APInt({w}, words)); |
372 | | - }} |
373 | | -}}; |
374 | | -extern "C" {{ |
375 | | - void {exported_symbol}({name} *out, FuzzOp<{name}> op) {{ |
376 | | - *out = op.eval(); |
377 | | - }} |
378 | | -}}"# |
379 | | - ) |
380 | | - }) |
381 | | - .collect::<String>() |
382 | | -} |
0 commit comments