Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit 21eb727

Browse files
committed
Add return value accessor to assigner
At the end of evaluation return value of circuit function will be stored and can be accessed afterwards.
1 parent 677d874 commit 21eb727

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

include/nil/blueprint/assigner.hpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,10 @@ namespace nil {
15741574
// Final return
15751575
finished = true;
15761576

1577+
if (gen_mode.has_assignments()) {
1578+
fill_return_value(llvm::cast<llvm::ReturnInst>(inst), frame);
1579+
}
1580+
15771581
if(print_output_format != no_print && gen_mode.has_assignments()) {
15781582
if(print_output_format == hex) {
15791583
std::cout << std::hex;
@@ -1689,6 +1693,80 @@ namespace nil {
16891693
return detail::var_value<BlueprintFieldType, var>(input_var, assignments[currProverIdx], internal_storage, gen_mode.has_assignments());
16901694
}
16911695

1696+
/**
1697+
* @brief Fill return value of the circuit function.
1698+
*
1699+
* @param inst pointer to `ret` instruction of circuit function
1700+
* @param frame reference to frame of circuit function
1701+
*/
1702+
void fill_return_value(const llvm::ReturnInst *inst, stack_frame<var> &frame) {
1703+
auto ret_val = inst->getReturnValue();
1704+
if (ret_val == nullptr) {
1705+
// Function returns void
1706+
return_value = {};
1707+
return;
1708+
}
1709+
auto ret_type = ret_val->getType();
1710+
switch (ret_type->getTypeID()) {
1711+
case llvm::Type::IntegerTyID: {
1712+
return_value = {
1713+
typename BlueprintFieldType::integral_type(get_var_value(frame.scalars[ret_val]).data)
1714+
};
1715+
break;
1716+
}
1717+
case llvm::Type::GaloisFieldTyID: {
1718+
auto field_type = llvm::cast<llvm::GaloisFieldType>(ret_type);
1719+
if (field_arg_num<BlueprintFieldType>(field_type) == 1) {
1720+
// Native field case
1721+
return_value = {
1722+
typename BlueprintFieldType::integral_type(get_var_value(frame.scalars[ret_val]).data)
1723+
};
1724+
} else {
1725+
llvm::GaloisFieldKind field_kind = field_type->getFieldKind();
1726+
std::vector<var> res = frame.vectors[ret_val];
1727+
std::vector<typename BlueprintFieldType::value_type> chopped_field;
1728+
for (std::size_t i = 0; i < res.size(); i++) {
1729+
chopped_field.push_back(get_var_value(res[i]));
1730+
}
1731+
return_value = {
1732+
unmarshal_field_val<BlueprintFieldType>(field_kind, chopped_field)
1733+
};
1734+
}
1735+
break;
1736+
}
1737+
case llvm::Type::EllipticCurveTyID: {
1738+
auto curve_type = llvm::cast<llvm::EllipticCurveType>(ret_type);
1739+
std::vector<var> res = frame.vectors[ret_val];
1740+
size_t curve_len = curve_arg_num<BlueprintFieldType>(ret_val->getType());
1741+
if (curve_len == 2) {
1742+
// Native curve
1743+
return_value = {
1744+
typename BlueprintFieldType::integral_type(get_var_value(res[0]).data),
1745+
typename BlueprintFieldType::integral_type(get_var_value(res[1]).data),
1746+
};
1747+
} else {
1748+
// Non-native curve
1749+
llvm::GaloisFieldKind field_kind = curve_type->GetBaseFieldKind();
1750+
std::vector<var> res = frame.vectors[ret_val];
1751+
column_type<BlueprintFieldType> chopped_field_x;
1752+
column_type<BlueprintFieldType> chopped_field_y;
1753+
for (std::size_t i = 0; i < curve_len / 2; i++) {
1754+
chopped_field_x.push_back(get_var_value(res[i]));
1755+
chopped_field_y.push_back(get_var_value(res[i + (curve_len / 2)]));
1756+
}
1757+
return_value = {
1758+
unmarshal_field_val<BlueprintFieldType>(field_kind, chopped_field_x),
1759+
unmarshal_field_val<BlueprintFieldType>(field_kind, chopped_field_y),
1760+
};
1761+
}
1762+
}
1763+
default: {
1764+
// Do nothing, just leave return value empty.
1765+
return_value = {};
1766+
}
1767+
}
1768+
}
1769+
16921770
public:
16931771
bool parse_ir_file(const char *ir_file) {
16941772
llvm::SMDiagnostic diagnostic;
@@ -1804,6 +1882,25 @@ namespace nil {
18041882
}
18051883
}
18061884

1885+
/**
1886+
* @brief Get return value of circuit function.
1887+
*
1888+
* Returns undefined value if evaluation did not finished successfully.
1889+
*
1890+
* Non-native field/curve types are represented with single value and truncated to
1891+
* match the native size.
1892+
*/
1893+
std::vector<typename BlueprintFieldType::integral_type> get_return_value() {
1894+
// TODO: this must be removed after completing implementation of `fill_return_value`
1895+
auto ret_type = circuit_function->getReturnType();
1896+
if (finished && !ret_type->isVoidTy() && return_value.empty()) {
1897+
LLVM_PRINT(ret_type, str);
1898+
TODO_WITH_LINK("accessing return value for " + str + " type",
1899+
"https://github.com/NilFoundation/zkllvm-assigner/issues/218");
1900+
}
1901+
return return_value;
1902+
}
1903+
18071904
private:
18081905
llvm::LLVMContext context;
18091906
const llvm::BasicBlock *predecessor = nullptr;
@@ -1834,6 +1931,7 @@ namespace nil {
18341931
* identified as constant column with special internal_storage_index = std::numeric_limits<std::size_t>::max()
18351932
***/
18361933
column_type<BlueprintFieldType> internal_storage;
1934+
std::vector<typename BlueprintFieldType::integral_type> return_value;
18371935
};
18381936

18391937
} // namespace blueprint

0 commit comments

Comments
 (0)