+1 data point: while writing approximations for math functions we had a style/design questions: use generic builder API or custom-built DSL/builders.
Writing math function approximations is basically expanding high level ops like Log into sequence of primitive add/mul and bitcasting, and it is pretty dense, and hard to follow, and in my opinion default builders API adds too much boilerplate and makes it even harder.
Discussion is in: https://reviews.llvm.org/D97146
With custom builder std::frexp
can be implemented with:
Value cst126f = f32Cst(126.0f);
Value cstHalf = f32Cst(0.5f);
Value cstInvMantMask = f32FromBits(~0x7f800000u);
// Cast to i32 for bitwise operations.
Value i32Half = bitcast(i32, cstHalf);
Value i32InvMantMask = bitcast(i32, cstInvMantMask);
Value i32Arg = bitcast(i32, arg);
// Compute normalized fraction.
Value tmp0 = bitwiseOr(bitwiseAnd(i32Arg, i32InvMantMask), i32Half);
Value normalized_fraction = bitcast(f32, tmp0);
// Compute exponent.
Value tmp1 = castSiToFp(f32, logicalShiftRight(bitcast(i32, abs(arg)), 23));
Value exponent = sub(tmp1, cst126f);
return {normalized_fraction, exponent};
after multiple iterations my best ImplicitLocOpBuilder
version is:
int width = vectorWidth(arg.getType());
auto bcast = [&](Value value) -> Value {
return broadcast(builder, value, width);
};
auto i32 = builder.getIntegerType(32);
auto i32Vec = broadcast(i32, width);
auto f32Vec = broadcast(builder.getF32Type(), width);
Value cst126f = f32Cst(builder, 126.0f);
Value cstHalf = f32Cst(builder, 0.5f);
Value cstInvMantMask = f32FromBits(builder, ~0x7f800000u);
// Cast to i32 for bitwise operations.
Value i32Half = builder.create<LLVM::BitcastOp>(i32, cstHalf);
Value i32InvMantMask = builder.create<LLVM::BitcastOp>(i32, cstInvMantMask);
Value i32Arg = builder.create<LLVM::BitcastOp>(i32Vec, arg);
// Compute normalized fraction.
Value tmp0 = builder.create<LLVM::AndOp>(i32Arg, bcast(i32InvMantMask));
Value tmp1 = builder.create<LLVM::OrOp>(tmp0, bcast(i32Half));
Value normalizedFraction = builder.create<LLVM::BitcastOp>(f32Vec, tmp1);
// Compute exponent.
Value biasedExponentBits = builder.create<UnsignedShiftRightOp>(
builder.create<LLVM::BitcastOp>(i32Vec, builder.create<AbsFOp>(arg)),
bcast(i32Cst(builder, 23)));
Value biasedExponent = builder.create<SIToFPOp>(f32Vec, biasedExponentBits);
Value exponent = builder.create<SubFOp>(biasedExponent, bcast(cst126f));
return {normalizedFraction, exponent};
Although it is not that bad as my original attempt to use builders (without implicit loc), it is a bit hard to get the gist of whatâs happening here. And there are many similar functions that have even more bitcasting and bitshifting.