Skip to content

Commit

Permalink
GDV-43: [C++] Introduce error codes as error handling strategy. (apac…
Browse files Browse the repository at this point in the history
…he#8)

* GDV-43: [C++] Introduce error codes as error handling strategy.

Introduced status codes and using the same as the error handling strategy.

The decision was taken to accommodate existing libraries that use error codes and
because Arrow also uses error codes and not exception.

Changed the signatures across the board for the same.
  • Loading branch information
praveenbingo authored May 31, 2018
1 parent 366e2c2 commit c3b6ce4
Show file tree
Hide file tree
Showing 15 changed files with 503 additions and 174 deletions.
8 changes: 5 additions & 3 deletions include/gandiva/evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <vector>
#include "gandiva/arrow.h"
#include "gandiva/expression.h"
#include "gandiva/status.h"

namespace gandiva {

Expand All @@ -33,9 +34,10 @@ class LLVMGenerator;
class Evaluator {
public:
/// Build an evaluator for the given schema to evaluate the vector of expressions.
static std::shared_ptr<Evaluator> Make(SchemaPtr schema,
const ExpressionVector &exprs,
arrow::MemoryPool *pool);
static Status Make(SchemaPtr schema,
const ExpressionVector &exprs,
arrow::MemoryPool *pool,
std::shared_ptr<Evaluator> *evaluator);

/// Evaluate the specified record batch, and fill the output vectors.
/// TODO : need a zero-copy variant if the caller can alloc the output vectors.
Expand Down
166 changes: 166 additions & 0 deletions include/gandiva/status.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright (C) 2017-2018 Dremio Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Adapted from Apache Arrow Status.
*/
#ifndef GANDIVA_STATUS_H
#define GANDIVA_STATUS_H

#include <string>
#include <utility>

#define GANDIVA_RETURN_NOT_OK(status) \
do { \
Status _status = (status); \
if (!_status.ok()) { \
std::stringstream ss; \
ss << __FILE__ << ":" << __LINE__ << " code: " << #status << "\n" << _status.message(); \
return Status(_status.code(), ss.str()); \
} \
} while (0)

#define GANDIVA_RETURN_FAILURE_IF_FALSE(condition, status) \
do { \
if (!condition) { \
Status _status = (status); \
std::stringstream ss; \
ss << __FILE__ << ":" << __LINE__ << " code: " << #status << "\n" << _status.message(); \
return Status(_status.code(), ss.str()); \
} \
} while (0)

namespace gandiva {

enum class StatusCode : char {
OK = 0,
CodeGenError = 1
};

class Status {
public:
// Create a success status.
Status() : state_(NULL) {}
~Status() { delete state_; }

Status(StatusCode code, const std::string& msg);

// Copy the specified status.
Status(const Status& s);
Status& operator=(const Status& s);

// Move the specified status.
Status(Status&& s);
Status& operator=(Status&& s);

// AND the statuses.
Status operator&(const Status& s) const;
Status operator&(Status&& s) const;
Status& operator&=(const Status& s);
Status& operator&=(Status&& s);

// Return a success status.
static Status OK() { return Status(); }

// Return error status of an appropriate type.
static Status CodeGenError(const std::string& msg) {
return Status(StatusCode::CodeGenError, msg);
}

// Returns true if the status indicates success.
bool ok() const { return (state_ == NULL); }

bool IsCodeGenError() const { return code() == StatusCode::CodeGenError; }

// Return a string representation of this status suitable for printing.
// Returns the string "OK" for success.
std::string ToString() const;

// Return a string representation of the status code, without the message
// text or posix code information.
std::string CodeAsString() const;

StatusCode code() const { return ok() ? StatusCode::OK : state_->code; }

std::string message() const { return ok() ? "" : state_->msg; }

private:
struct State {
StatusCode code;
std::string msg;
};
// OK status has a `NULL` state_. Otherwise, `state_` points to
// a `State` structure containing the error code and message(s)
State* state_;

void CopyFrom(const Status& s);
void MoveFrom(Status& s);
};

static inline std::ostream& operator<<(std::ostream& os, const Status& x) {
os << x.ToString();
return os;
}

inline Status::Status(const Status& s)
: state_((s.state_ == NULL) ? NULL : new State(*s.state_)) {}

inline Status& Status::operator=(const Status& s) {
// The following condition catches both aliasing (when this == &s),
// and the common case where both s and *this are ok.
if (state_ != s.state_) {
CopyFrom(s);
}
return *this;
}

inline Status::Status(Status&& s) : state_(s.state_) { s.state_ = NULL; }

inline Status& Status::operator=(Status&& s) {
MoveFrom(s);
return *this;
}

inline Status Status::operator&(const Status& s) const {
if (ok()) {
return s;
} else {
return *this;
}
}

inline Status Status::operator&(Status&& s) const {
if (ok()) {
return std::move(s);
} else {
return *this;
}
}

inline Status& Status::operator&=(const Status& s) {
if (ok() && !s.ok()) {
CopyFrom(s);
}
return *this;
}

inline Status& Status::operator&=(Status&& s) {
if (ok() && !s.ok()) {
MoveFrom(s);
}
return *this;
}

} // namespace gandiva
#endif // GANDIVA_STATUS_H
6 changes: 4 additions & 2 deletions src/codegen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ add_library(gandiva SHARED
llvm_types.cc
node.cc
tree_expr_builder.cc
status.cc
${BC_FILE_PATH_CC})

# For users of gandiva library (including integ tests), include-dir is :
Expand Down Expand Up @@ -85,12 +86,13 @@ install(

#args: label test-file src-files
add_gandiva_unit_test(dex_llvm_test.cc)
add_gandiva_unit_test(engine_llvm_test.cc engine.cc llvm_types.cc ${BC_FILE_PATH_CC})
add_gandiva_unit_test(engine_llvm_test.cc engine.cc llvm_types.cc status.cc ${BC_FILE_PATH_CC})
add_gandiva_unit_test(function_signature_test.cc)
add_gandiva_unit_test(function_registry_test.cc function_registry.cc)
add_gandiva_unit_test(llvm_types_test.cc llvm_types.cc)
add_gandiva_unit_test(llvm_generator_test.cc llvm_generator.cc engine.cc llvm_types.cc function_registry.cc annotator.cc ${BC_FILE_PATH_CC})
add_gandiva_unit_test(llvm_generator_test.cc llvm_generator.cc engine.cc llvm_types.cc function_registry.cc annotator.cc status.cc ${BC_FILE_PATH_CC})
add_gandiva_unit_test(annotator_test.cc annotator.cc)
add_gandiva_unit_test(tree_expr_test.cc tree_expr_builder.cc node.cc annotator.cc function_registry.cc)
add_gandiva_unit_test(status_test.cc status.cc)

add_gandiva_integ_test(evaluator_test.cc)
35 changes: 0 additions & 35 deletions src/codegen/codegen_exception.h

This file was deleted.

52 changes: 29 additions & 23 deletions src/codegen/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
#include <string>
#include <unordered_set>
#include <utility>
#include "codegen/codegen_exception.h"
#include "codegen/engine.h"

namespace gandiva {
Expand All @@ -58,41 +57,47 @@ void Engine::InitOnce() {
init_once_done_ = true;
}

Engine::Engine()
: module_finalized_(false) {
std::call_once(init_once_flag, InitOnce);
context_.reset(new llvm::LLVMContext());
ir_builder_.reset(new llvm::IRBuilder<>(*context()));
/// factory method to construct the engine.
Status Engine::Make(std::unique_ptr<Engine> *engine) {
std::unique_ptr<Engine> engine_obj(new Engine());

std::call_once(init_once_flag, [&engine_obj] {engine_obj->InitOnce();});
engine_obj->context_.reset(new llvm::LLVMContext());
engine_obj->ir_builder_.reset(new llvm::IRBuilder<>(*(engine_obj->context())));

/* Create the execution engine */
std::unique_ptr<llvm::Module> cg_module(new llvm::Module("codegen", *context()));
module_ = cg_module.get();
std::unique_ptr<llvm::Module> cg_module(new llvm::Module("codegen",
*(engine_obj->context())));
engine_obj->module_ = cg_module.get();

llvm::EngineBuilder engineBuilder(std::move(cg_module));
engineBuilder.setEngineKind(llvm::EngineKind::JIT);
engineBuilder.setOptLevel(llvm::CodeGenOpt::Aggressive);
engineBuilder.setErrorStr(&llvm_error_);
execution_engine_.reset(engineBuilder.create());
if (execution_engine_ == NULL) {
module_ = NULL;
throw CodeGenException(llvm_error_);
engineBuilder.setErrorStr(&(engine_obj->llvm_error_));
engine_obj->execution_engine_.reset(engineBuilder.create());
if (engine_obj->execution_engine_ == NULL) {
engine_obj->module_ = NULL;
return Status::CodeGenError(engine_obj->llvm_error_);
}

LoadPreCompiledIRFiles();
Status result = engine_obj->LoadPreCompiledIRFiles();
GANDIVA_RETURN_NOT_OK(result);
*engine = std::move(engine_obj);
return Status::OK();
}

/*
* Handling for pre-compiled IR libraries.
*/
void Engine::LoadPreCompiledIRFiles() {
Status Engine::LoadPreCompiledIRFiles() {
/// Read from file into memory buffer.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer_or_error =
llvm::MemoryBuffer::getFile(kByteCodeFilePath);
if (!buffer_or_error) {
std::stringstream ss;
ss << "Could not load module from IR " << kByteCodeFilePath << ": " <<
buffer_or_error.getError().message();
throw CodeGenException(ss.str());
return Status::CodeGenError(ss.str());
}
std::unique_ptr<llvm::MemoryBuffer> buffer = move(buffer_or_error.get());

Expand All @@ -104,26 +109,26 @@ void Engine::LoadPreCompiledIRFiles() {
llvm::handleAllErrors(module_or_error.takeError(), [&](llvm::ErrorInfoBase &eib) {
error_string = eib.message();
});
throw CodeGenException(error_string);
return Status::CodeGenError(error_string);
}
std::unique_ptr<llvm::Module> ir_module = move(module_or_error.get());

/// Verify the IR module
if (llvm::verifyModule(*ir_module.get(), &llvm::errs())) {
throw CodeGenException("verify of IR Module failed");
return Status::CodeGenError("verify of IR Module failed");
}

// Link this to the primary module.
if (llvm::Linker::linkModules(*module_, move(ir_module))) {
throw CodeGenException("failed to link IR Modules");
return Status::CodeGenError("failed to link IR Modules");
}
return Status::OK();
}

/*
* Optimise and compile the module.
*/
void
Engine::FinalizeModule(bool optimise_ir, bool dump_ir) {
Status Engine::FinalizeModule(bool optimise_ir, bool dump_ir) {
if (dump_ir) {
DumpIR("Before optimise");
}
Expand Down Expand Up @@ -178,16 +183,17 @@ Engine::FinalizeModule(bool optimise_ir, bool dump_ir) {
}

if (llvm::verifyModule(*module_, &llvm::errs())) {
throw CodeGenException("verify of module failed after optimisation passes");
return Status::CodeGenError("verify of module failed after optimisation passes");
}

// do the compilation
execution_engine_->finalizeObject();
module_finalized_ = true;
return Status::OK();
}

void *Engine::CompiledFunction(llvm::Function *irFunction) {
assert(module_finalized_);
DCHECK(module_finalized_);
return execution_engine_->getPointerToFunction(irFunction);
}

Expand Down
Loading

0 comments on commit c3b6ce4

Please sign in to comment.