From 32fa16763c84b3446c1ecf9494aaccac217ecbee Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Sun, 21 Jun 2026 11:57:04 +0800 Subject: [PATCH] Add support for clmul --- ir/instr.cpp | 6 ++++++ ir/instr.h | 2 +- llvm_util/llvm2alive.cpp | 4 +++- smt/expr.cpp | 12 +++++++++++ smt/expr.h | 1 + tests/alive-tv/clmul.srctgt.ll | 20 +++++++++++++++++ tests/unit/clmul.opt | 39 ++++++++++++++++++++++++++++++++++ tools/alive_lexer.re | 1 + tools/alive_parser.cpp | 3 +++ tools/tokens.h | 1 + 10 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 tests/alive-tv/clmul.srctgt.ll create mode 100644 tests/unit/clmul.opt diff --git a/ir/instr.cpp b/ir/instr.cpp index 7dab68ab0..8bd186189 100644 --- a/ir/instr.cpp +++ b/ir/instr.cpp @@ -186,6 +186,7 @@ void BinOp::print(ostream &os) const { case Abs: str = "abs "; break; case UCmp: str = "ucmp "; break; case SCmp: str = "scmp "; break; + case Clmul: str = "clmul "; break; } os << getName() << " = " << str; @@ -473,6 +474,11 @@ StateValue BinOp::toSMT(State &s) const { ap && bp}; }; break; + case Clmul: + fn = [&](auto &a, auto &ap, auto &b, auto &bp) -> StateValue { + return {a.clmul(b), ap && bp}; + }; + break; } function(const expr&, const expr&, const expr&, diff --git a/ir/instr.h b/ir/instr.h index ed2f3bf5c..2ff8cedba 100644 --- a/ir/instr.h +++ b/ir/instr.h @@ -41,7 +41,7 @@ class BinOp final : public Instr { SAdd_Overflow, UAdd_Overflow, SSub_Overflow, USub_Overflow, SMul_Overflow, UMul_Overflow, And, Or, Xor, Cttz, Ctlz, UMin, UMax, SMin, SMax, Abs, - UCmp, SCmp }; + UCmp, SCmp, Clmul }; enum Flags { None = 0, NSW = 1 << 0, NUW = 1 << 1, Exact = 1 << 2, Disjoint = 1 << 3 }; private: diff --git a/llvm_util/llvm2alive.cpp b/llvm_util/llvm2alive.cpp index 6e6ef42b1..afd4efd8d 100644 --- a/llvm_util/llvm2alive.cpp +++ b/llvm_util/llvm2alive.cpp @@ -840,7 +840,8 @@ class llvm2alive_ : public llvm::InstVisitor> { case llvm::Intrinsic::smax: case llvm::Intrinsic::abs: case llvm::Intrinsic::ucmp: - case llvm::Intrinsic::scmp: { + case llvm::Intrinsic::scmp: + case llvm::Intrinsic::clmul: { PARSE_BINOP(); addNoundefAssumes(i, {a, b}); BinOp::Op op; @@ -866,6 +867,7 @@ class llvm2alive_ : public llvm::InstVisitor> { case llvm::Intrinsic::abs: op = BinOp::Abs; break; case llvm::Intrinsic::ucmp: op = BinOp::UCmp; break; case llvm::Intrinsic::scmp: op = BinOp::SCmp; break; + case llvm::Intrinsic::clmul: op = BinOp::Clmul; break; default: UNREACHABLE(); } ret = make_unique(*ty, value_name(i), *a, *b, op); diff --git a/smt/expr.cpp b/smt/expr.cpp index 2abdd5a25..14ef93559 100644 --- a/smt/expr.cpp +++ b/smt/expr.cpp @@ -1124,6 +1124,18 @@ expr expr::ctpop() const { return res; } +expr expr::clmul(const expr &rhs) const { + C(rhs); + auto nbits = bits(); + + auto res = mkUInt(0, sort()); + for (unsigned i = 0; i < nbits; ++i) { + res = res ^ (extract(i, i).sext(nbits - 1) & (rhs << mkUInt(i, sort()))); + } + + return res; +} + expr expr::umin(const expr &rhs) const { return mkIf(ule(rhs), *this, rhs); } diff --git a/smt/expr.h b/smt/expr.h index f34e1efb5..5cd797521 100644 --- a/smt/expr.h +++ b/smt/expr.h @@ -217,6 +217,7 @@ class expr { expr cttz(const expr &val_zero) const; expr ctlz() const; expr ctpop() const; + expr clmul(const expr &rhs) const; expr umin(const expr &rhs) const; expr umax(const expr &rhs) const; diff --git a/tests/alive-tv/clmul.srctgt.ll b/tests/alive-tv/clmul.srctgt.ll new file mode 100644 index 000000000..7c30f770a --- /dev/null +++ b/tests/alive-tv/clmul.srctgt.ll @@ -0,0 +1,20 @@ +; clmulr(x, y) +; = clmul(zext(x, 2 * BW), zext(y, 2 * BW)) >> (BW - 1) +; = bitreverse(clmul(bitreverse(x))) + +define i8 @src(i8 %x, i8 %y) { + %extx = zext i8 %x to i16 + %exty = zext i8 %y to i16 + %clmul = call i16 @llvm.clmul.i16(i16 %extx, i16 %exty) + %clmulr = lshr i16 %clmul, 7 + %trunc = trunc i16 %clmulr to i8 + ret i8 %trunc +} + +define i8 @tgt(i8 %x, i8 %y) { + %rx = call i8 @llvm.bitreverse.i8(i8 %x) + %ry = call i8 @llvm.bitreverse.i8(i8 %y) + %clmul = call i8 @llvm.clmul.i8(i8 %rx, i8 %ry) + %clmulr = call i8 @llvm.bitreverse.i8(i8 %clmul) + ret i8 %clmulr +} diff --git a/tests/unit/clmul.opt b/tests/unit/clmul.opt new file mode 100644 index 000000000..b7c504747 --- /dev/null +++ b/tests/unit/clmul.opt @@ -0,0 +1,39 @@ +Name: clmul constant1 +%r = clmul i4 1, i4 2 + => +%r = 2 + +Name: clmul constant2 +%r = clmul i4 5, i4 6 + => +%r = 14 + +Name: clmul constant3 +%r = clmul i4 -4, i4 2 + => +%r = -8 + +Name: clmul constant4 +%r = clmul i4 -4, i4 -5 + => +%r = 4 + +Name: clmul constant5 +%r = clmul i8 0, i8 255 + => +%r = 0 + +Name: clmul constant6 +%r = clmul i8 15, i8 15 + => +%r = 85 + +Name: clmul constant7 +%r = clmul i8 1, i8 2 + => +%r = 2 + +Name: clmul constant8 +%r = clmul i16 -1, i16 -2 + => +%r = -21846 diff --git a/tools/alive_lexer.re b/tools/alive_lexer.re index 0b5784373..d16e11932 100644 --- a/tools/alive_lexer.re +++ b/tools/alive_lexer.re @@ -286,6 +286,7 @@ space+ { "abs" { return ABS; } "ucmp" { return UCMP; } "scmp" { return SCMP; } +"clmul" { return CLMUL; } "oeq" { return OEQ; } "ogt" { return OGT; } "oge" { return OGE; } diff --git a/tools/alive_parser.cpp b/tools/alive_parser.cpp index a9ddb752e..cd2ca7e39 100644 --- a/tools/alive_parser.cpp +++ b/tools/alive_parser.cpp @@ -710,6 +710,7 @@ static unsigned parse_binop_flags(token op_token) { case ABS: case UCMP: case SCMP: + case CLMUL: return BinOp::None; default: UNREACHABLE(); @@ -782,6 +783,7 @@ static unique_ptr parse_binop(string_view name, token op_token) { case ABS: op = BinOp::Abs; break; case UCMP: op = BinOp::UCmp; break; case SCMP: op = BinOp::SCmp; break; + case CLMUL: op = BinOp::Clmul; break; default: UNREACHABLE(); } @@ -1273,6 +1275,7 @@ static unique_ptr parse_instr(string_view name) { case ABS: case UCMP: case SCMP: + case CLMUL: return parse_binop(name, t); case FADD: case FSUB: diff --git a/tools/tokens.h b/tools/tokens.h index aee48bbd2..c02fd9c72 100644 --- a/tools/tokens.h +++ b/tools/tokens.h @@ -15,6 +15,7 @@ TOKEN(BITREVERSE) TOKEN(BSWAP) TOKEN(BOR) TOKEN(CALL) +TOKEN(CLMUL) TOKEN(COMMA) TOKEN(CONSTANT) TOKEN(CEQ)