Skip to content
This repository was archived by the owner on May 9, 2024. It is now read-only.

Commit 32ed024

Browse files
committed
Add support for bitwise operations.
Signed-off-by: ienkovich <[email protected]>
1 parent ae22e1a commit 32ed024

File tree

15 files changed

+809
-1
lines changed

15 files changed

+809
-1
lines changed

omniscidb/Analyzer/Analyzer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,14 @@ const hdk::ir::Type* analyze_type_info(hdk::ir::OpType op,
303303
}
304304
}
305305
result_type = common_type;
306+
} else if (hdk::ir::isBitwise(op)) {
307+
if (!left_type->isInteger() || !right_type->isInteger()) {
308+
throw std::runtime_error("non-integer operands in bitwise operation.");
309+
}
310+
common_type = common_numeric_type(left_type, right_type);
311+
*new_left_type = common_type->withNullable(left_type->nullable());
312+
*new_right_type = common_type->withNullable(right_type->nullable());
313+
result_type = common_type;
306314
} else {
307315
throw std::runtime_error("invalid binary operator type.");
308316
}
@@ -646,6 +654,9 @@ hdk::ir::ExprPtr normalizeOperExpr(const hdk::ir::OpType optype,
646654
left_expr = left_expr->decompress();
647655
right_expr = right_expr->decompress();
648656
}
657+
} else if (hdk::ir::isBitwise(optype)) {
658+
left_expr = left_expr->cast(new_left_type);
659+
right_expr = right_expr->cast(new_right_type);
649660
} else if (!hdk::ir::isComparison(optype)) {
650661
left_expr = left_expr->decompress();
651662
right_expr = right_expr->decompress();

omniscidb/IR/Expr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ class UOper : public Expr {
293293
OpType opType() const { return op_type_; }
294294

295295
bool isNot() const { return op_type_ == OpType::kNot; }
296+
bool isBwNot() const { return op_type_ == OpType::kBwNot; }
296297
bool isUMinus() const { return op_type_ == OpType::kUMinus; }
297298
bool isIsNull() const { return op_type_ == OpType::kIsNull; }
298299
bool isCast() const { return op_type_ == OpType::kCast; }
@@ -342,6 +343,9 @@ class BinOper : public Expr {
342343
bool isGe() const { return op_type_ == OpType::kGe; }
343344
bool isAnd() const { return op_type_ == OpType::kAnd; }
344345
bool isOr() const { return op_type_ == OpType::kOr; }
346+
bool isBwAnd() const { return op_type_ == OpType::kBwAnd; }
347+
bool isBwOr() const { return op_type_ == OpType::kBwOr; }
348+
bool isBwXor() const { return op_type_ == OpType::kBwXor; }
345349
bool isMinus() const { return op_type_ == OpType::kMinus; }
346350
bool isPlus() const { return op_type_ == OpType::kPlus; }
347351
bool isMul() const { return op_type_ == OpType::kMul; }
@@ -353,6 +357,7 @@ class BinOper : public Expr {
353357
bool isComparison() const { return hdk::ir::isComparison(op_type_); }
354358
bool isLogic() const { return hdk::ir::isLogic(op_type_); }
355359
bool isArithmetic() const { return hdk::ir::isArithmetic(op_type_); }
360+
bool isBitwise() const { return hdk::ir::isBitwise(op_type_); }
356361

357362
Qualifier qualifier() const { return qualifier_; }
358363
const Expr* leftOperand() const { return left_operand_.get(); }

omniscidb/IR/OpType.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ enum class OpType {
2424
kAnd,
2525
kOr,
2626
kNot,
27+
kBwAnd,
28+
kBwOr,
29+
kBwXor,
30+
kBwNot,
2731
kMinus,
2832
kPlus,
2933
kMul,
@@ -60,12 +64,17 @@ inline OpType commuteComparison(OpType op) {
6064
}
6165
inline bool isUnary(OpType op) {
6266
return op == OpType::kNot || op == OpType::kUMinus || op == OpType::kIsNull ||
63-
op == OpType::kCast;
67+
op == OpType::kCast || op == OpType::kBwNot;
6468
}
6569
inline bool isEquivalence(OpType op) {
6670
return op == OpType::kEq || op == OpType::kBwEq;
6771
}
6872

73+
inline bool isBitwise(OpType op) {
74+
return op == OpType::kBwAnd || op == OpType::kBwOr || op == OpType::kBwXor ||
75+
op == OpType::kBwNot;
76+
}
77+
6978
enum class Qualifier { kOne, kAny, kAll };
7079

7180
enum class AggType {
@@ -126,6 +135,14 @@ inline std::string toString(hdk::ir::OpType op) {
126135
return "OR";
127136
case hdk::ir::OpType::kNot:
128137
return "NOT";
138+
case hdk::ir::OpType::kBwAnd:
139+
return "BW_AND";
140+
case hdk::ir::OpType::kBwOr:
141+
return "BW_OR";
142+
case hdk::ir::OpType::kBwXor:
143+
return "BW_XOR";
144+
case hdk::ir::OpType::kBwNot:
145+
return "BW_NOT";
129146
case hdk::ir::OpType::kMinus:
130147
return "MINUS";
131148
case hdk::ir::OpType::kPlus:

omniscidb/QueryBuilder/QueryBuilder.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,84 @@ BuilderExpr BuilderExpr::logicalOr(const BuilderExpr& rhs) const {
12901290
}
12911291
}
12921292

1293+
BuilderExpr BuilderExpr::bwAnd(const BuilderExpr& rhs) const {
1294+
try {
1295+
auto bin_oper = Analyzer::normalizeOperExpr(
1296+
OpType::kBwAnd, Qualifier::kOne, expr_, rhs.expr(), nullptr);
1297+
return {builder_, bin_oper, "", true};
1298+
} catch (std::runtime_error& e) {
1299+
throw InvalidQueryError() << "Cannot apply BW_AND operation for operand types "
1300+
<< expr_->type()->toString() << " and "
1301+
<< rhs.expr()->type()->toString();
1302+
}
1303+
}
1304+
1305+
BuilderExpr BuilderExpr::bwAnd(int val) const {
1306+
return bwAnd(builder_->cst(val, builder_->ctx_.int32(false)));
1307+
}
1308+
1309+
BuilderExpr BuilderExpr::bwAnd(int64_t val) const {
1310+
return bwAnd(builder_->cst(val, builder_->ctx_.int64(false)));
1311+
}
1312+
1313+
BuilderExpr BuilderExpr::bwOr(const BuilderExpr& rhs) const {
1314+
try {
1315+
auto bin_oper = Analyzer::normalizeOperExpr(
1316+
OpType::kBwOr, Qualifier::kOne, expr_, rhs.expr(), nullptr);
1317+
return {builder_, bin_oper, "", true};
1318+
} catch (std::runtime_error& e) {
1319+
throw InvalidQueryError() << "Cannot apply BW_OR operation for operand types "
1320+
<< expr_->type()->toString() << " and "
1321+
<< rhs.expr()->type()->toString();
1322+
}
1323+
}
1324+
1325+
BuilderExpr BuilderExpr::bwOr(int val) const {
1326+
return bwOr(builder_->cst(val, builder_->ctx_.int32(false)));
1327+
}
1328+
1329+
BuilderExpr BuilderExpr::bwOr(int64_t val) const {
1330+
return bwOr(builder_->cst(val, builder_->ctx_.int64(false)));
1331+
}
1332+
1333+
BuilderExpr BuilderExpr::bwXor(const BuilderExpr& rhs) const {
1334+
try {
1335+
auto bin_oper = Analyzer::normalizeOperExpr(
1336+
OpType::kBwXor, Qualifier::kOne, expr_, rhs.expr(), nullptr);
1337+
return {builder_, bin_oper, "", true};
1338+
} catch (std::runtime_error& e) {
1339+
throw InvalidQueryError() << "Cannot apply BW_XOR operation for operand types "
1340+
<< expr_->type()->toString() << " and "
1341+
<< rhs.expr()->type()->toString();
1342+
}
1343+
}
1344+
1345+
BuilderExpr BuilderExpr::bwXor(int val) const {
1346+
return bwXor(builder_->cst(val, builder_->ctx_.int32(false)));
1347+
}
1348+
1349+
BuilderExpr BuilderExpr::bwXor(int64_t val) const {
1350+
return bwXor(builder_->cst(val, builder_->ctx_.int64(false)));
1351+
}
1352+
1353+
BuilderExpr BuilderExpr::bwNot() const {
1354+
if (!expr_->type()->isInteger()) {
1355+
throw InvalidQueryError("Only integer expressions are allowed for BW_NOT operation.");
1356+
}
1357+
1358+
if (expr_->is<Constant>()) {
1359+
auto cst_expr = expr_->as<Constant>();
1360+
if (cst_expr->isNull()) {
1361+
return *this;
1362+
}
1363+
return builder_->cst(~cst_expr->intVal(), cst_expr->type());
1364+
}
1365+
1366+
auto uoper =
1367+
makeExpr<UOper>(expr_->type(), expr_->containsAgg(), OpType::kBwNot, expr_);
1368+
return {builder_, uoper, "", true};
1369+
}
1370+
12931371
BuilderExpr BuilderExpr::eq(const BuilderExpr& rhs) const {
12941372
try {
12951373
auto bin_oper = Analyzer::normalizeOperExpr(

omniscidb/QueryBuilder/QueryBuilder.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,20 @@ class BuilderExpr {
156156
BuilderExpr logicalAnd(const BuilderExpr& rhs) const;
157157
BuilderExpr logicalOr(const BuilderExpr& rhs) const;
158158

159+
BuilderExpr bwAnd(const BuilderExpr& rhs) const;
160+
BuilderExpr bwAnd(int val) const;
161+
BuilderExpr bwAnd(int64_t val) const;
162+
163+
BuilderExpr bwOr(const BuilderExpr& rhs) const;
164+
BuilderExpr bwOr(int val) const;
165+
BuilderExpr bwOr(int64_t val) const;
166+
167+
BuilderExpr bwXor(const BuilderExpr& rhs) const;
168+
BuilderExpr bwXor(int val) const;
169+
BuilderExpr bwXor(int64_t val) const;
170+
171+
BuilderExpr bwNot() const;
172+
159173
BuilderExpr eq(const BuilderExpr& rhs) const;
160174
BuilderExpr eq(int val) const;
161175
BuilderExpr eq(int64_t val) const;

omniscidb/QueryEngine/ArithmeticIR.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,70 @@ llvm::Value* CodeGenerator::codegenArith(const hdk::ir::BinOper* bin_oper,
6868
return nullptr;
6969
}
7070

71+
llvm::Value* CodeGenerator::codegenBitwise(const hdk::ir::UOper* uoper,
72+
const CompilationOptions& co) {
73+
CHECK(uoper->opType() == hdk::ir::OpType::kBwNot);
74+
auto op = uoper->operand();
75+
auto op_type = op->type();
76+
CHECK(op_type->isInteger());
77+
auto op_lv = codegen(op, true, co).front();
78+
const auto int_typename = numeric_or_time_interval_type_name(op->type(), op_type);
79+
const auto null_check_suffix = get_null_check_suffix(op->type(), op_type);
80+
if (null_check_suffix.empty()) {
81+
return cgen_state_->ir_builder_.CreateNot(op_lv);
82+
} else {
83+
return cgen_state_->emitCall(
84+
"bw_not_" + int_typename + null_check_suffix,
85+
{op_lv, cgen_state_->llInt(inline_int_null_value(op_type))});
86+
}
87+
}
88+
89+
llvm::Value* CodeGenerator::codegenBitwise(const hdk::ir::BinOper* bin_oper,
90+
const CompilationOptions& co) {
91+
const auto lhs = bin_oper->leftOperand();
92+
const auto rhs = bin_oper->rightOperand();
93+
const auto& lhs_type = lhs->type();
94+
const auto& rhs_type = rhs->type();
95+
CHECK(lhs_type->isInteger());
96+
CHECK(rhs_type->isInteger());
97+
CHECK_EQ(lhs_type->size(), rhs_type->size());
98+
auto lhs_lv = codegen(lhs, true, co).front();
99+
auto rhs_lv = codegen(rhs, true, co).front();
100+
const auto int_typename = numeric_or_time_interval_type_name(lhs_type, rhs_type);
101+
const auto null_check_suffix = get_null_check_suffix(lhs_type, rhs_type);
102+
103+
if (null_check_suffix.empty()) {
104+
switch (bin_oper->opType()) {
105+
case hdk::ir::OpType::kBwAnd:
106+
return cgen_state_->ir_builder_.CreateAnd(lhs_lv, rhs_lv);
107+
case hdk::ir::OpType::kBwOr:
108+
return cgen_state_->ir_builder_.CreateOr(lhs_lv, rhs_lv);
109+
case hdk::ir::OpType::kBwXor:
110+
return cgen_state_->ir_builder_.CreateXor(lhs_lv, rhs_lv);
111+
default:
112+
CHECK(false);
113+
}
114+
}
115+
116+
std::string fn_name;
117+
switch (bin_oper->opType()) {
118+
case hdk::ir::OpType::kBwAnd:
119+
fn_name = "bw_and_";
120+
break;
121+
case hdk::ir::OpType::kBwOr:
122+
fn_name = "bw_or_";
123+
break;
124+
case hdk::ir::OpType::kBwXor:
125+
fn_name = "bw_xor_";
126+
break;
127+
default:
128+
CHECK(false);
129+
}
130+
return cgen_state_->emitCall(
131+
fn_name + int_typename + null_check_suffix,
132+
{lhs_lv, rhs_lv, cgen_state_->llInt(inline_int_null_value(lhs_type))});
133+
}
134+
71135
// Handle integer or integer-like (decimal, time, date) operand types.
72136
llvm::Value* CodeGenerator::codegenIntArith(const hdk::ir::BinOper* bin_oper,
73137
llvm::Value* lhs_lv,

omniscidb/QueryEngine/CalciteDeserializerUtils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ inline hdk::ir::OpType to_sql_op(const std::string& op_str) {
7474
if (op_str == std::string("NOT")) {
7575
return hdk::ir::OpType::kNot;
7676
}
77+
if (op_str == std::string("BW_AND") || op_str == std::string("BIT_AND")) {
78+
return hdk::ir::OpType::kBwAnd;
79+
}
80+
if (op_str == std::string("BW_OR") || op_str == std::string("BIT_OR")) {
81+
return hdk::ir::OpType::kBwOr;
82+
}
83+
if (op_str == std::string("BW_XOR") || op_str == std::string("BIT_XOR")) {
84+
return hdk::ir::OpType::kBwXor;
85+
}
86+
if (op_str == std::string("BW_NOT") || op_str == std::string("BIT_NOT")) {
87+
return hdk::ir::OpType::kBwNot;
88+
}
7789
if (op_str == std::string("IS NULL")) {
7890
return hdk::ir::OpType::kIsNull;
7991
}

omniscidb/QueryEngine/CodeGenerator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ class CodeGenerator {
127127

128128
llvm::Value* codegenArith(const hdk::ir::BinOper*, const CompilationOptions&);
129129

130+
llvm::Value* codegenBitwise(const hdk::ir::UOper*, const CompilationOptions&);
131+
llvm::Value* codegenBitwise(const hdk::ir::BinOper*, const CompilationOptions&);
132+
130133
llvm::Value* codegenUMinus(const hdk::ir::UOper*, const CompilationOptions&);
131134

132135
llvm::Value* codegenCmp(const hdk::ir::BinOper*, const CompilationOptions&);

omniscidb/QueryEngine/IRCodegen.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ llvm::Value* CodeGenerator::codegen(const hdk::ir::BinOper* bin_oper,
157157
if (bin_oper->isArithmetic()) {
158158
return codegenArith(bin_oper, co);
159159
}
160+
if (bin_oper->isBitwise()) {
161+
return codegenBitwise(bin_oper, co);
162+
}
160163
if (bin_oper->isComparison()) {
161164
return codegenCmp(bin_oper, co);
162165
}
@@ -188,6 +191,8 @@ llvm::Value* CodeGenerator::codegen(const hdk::ir::UOper* u_oper,
188191
}
189192
case hdk::ir::OpType::kUnnest:
190193
return codegenUnnest(u_oper, co);
194+
case hdk::ir::OpType::kBwNot:
195+
return codegenBitwise(u_oper, co);
191196
default:
192197
UNREACHABLE();
193198
}

omniscidb/QueryEngine/RuntimeFunctions.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@
4444

4545
// arithmetic operator implementations
4646

47+
#define DEF_UNARY_NULLABLE(type, null_type, opname, opsym) \
48+
extern "C" RUNTIME_EXPORT ALWAYS_INLINE type opname##_##type##_nullable( \
49+
const type op, const null_type null_val) { \
50+
if (op != null_val) { \
51+
return opsym op; \
52+
} \
53+
return null_val; \
54+
}
55+
4756
#define DEF_ARITH_NULLABLE(type, null_type, opname, opsym) \
4857
extern "C" RUNTIME_EXPORT ALWAYS_INLINE type opname##_##type##_nullable( \
4958
const type lhs, const type rhs, const null_type null_val) { \
@@ -187,6 +196,23 @@ DEF_ARITH_NULLABLE_RHS(int64_t, int64_t, mod, %)
187196
DEF_SAFE_INF_DIV_NULLABLE(float, float, safe_inf_div)
188197
DEF_SAFE_INF_DIV_NULLABLE(double, double, safe_inf_div)
189198

199+
#define DEF_ALL_NULLABLE_BW_OPS(type, null_type) \
200+
DEF_ARITH_NULLABLE(type, null_type, bw_and, &) \
201+
DEF_ARITH_NULLABLE_LHS(type, null_type, bw_and, &) \
202+
DEF_ARITH_NULLABLE_RHS(type, null_type, bw_and, &) \
203+
DEF_ARITH_NULLABLE(type, null_type, bw_or, |) \
204+
DEF_ARITH_NULLABLE_LHS(type, null_type, bw_or, |) \
205+
DEF_ARITH_NULLABLE_RHS(type, null_type, bw_or, |) \
206+
DEF_ARITH_NULLABLE(type, null_type, bw_xor, ^) \
207+
DEF_ARITH_NULLABLE_LHS(type, null_type, bw_xor, ^) \
208+
DEF_ARITH_NULLABLE_RHS(type, null_type, bw_xor, ^) \
209+
DEF_UNARY_NULLABLE(type, null_type, bw_not, ~)
210+
211+
DEF_ALL_NULLABLE_BW_OPS(int8_t, int64_t)
212+
DEF_ALL_NULLABLE_BW_OPS(int16_t, int64_t)
213+
DEF_ALL_NULLABLE_BW_OPS(int32_t, int64_t)
214+
DEF_ALL_NULLABLE_BW_OPS(int64_t, int64_t)
215+
190216
#undef DEF_BINARY_NULLABLE_ALL_OPS
191217
#undef DEF_SAFE_DIV_NULLABLE
192218
#undef DEF_CMP_NULLABLE_RHS

0 commit comments

Comments
 (0)