mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
commit
4b940f8bad
106 changed files with 3403 additions and 478 deletions
|
@ -179,6 +179,7 @@ set(FUNC_LIB_SOURCE
|
|||
func/stack-transform.cpp
|
||||
func/optimize.cpp
|
||||
func/codegen.cpp
|
||||
func/func.cpp
|
||||
)
|
||||
|
||||
set(TLB_BLOCK_AUTO
|
||||
|
@ -266,6 +267,8 @@ set(BIGINT_TEST_SOURCE
|
|||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE)
|
||||
|
||||
|
||||
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
|
||||
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
|
@ -305,13 +308,30 @@ target_include_directories(ton_block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SO
|
|||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api)
|
||||
|
||||
add_executable(func func/func.cpp ${FUNC_LIB_SOURCE})
|
||||
add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE})
|
||||
target_include_directories(func PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block)
|
||||
if (WINGETOPT_FOUND)
|
||||
target_link_libraries_system(func wingetopt)
|
||||
endif()
|
||||
|
||||
if (USE_EMSCRIPTEN)
|
||||
add_executable(funcfiftlib funcfiftlib/funcfiftlib.cpp ${FUNC_LIB_SOURCE})
|
||||
target_include_directories(funcfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
target_link_libraries(funcfiftlib PUBLIC fift-lib src_parser git)
|
||||
target_link_options(funcfiftlib PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,_malloc,free,UTF8ToString,stringToUTF8)
|
||||
target_link_options(funcfiftlib PRIVATE -sEXPORTED_FUNCTIONS=_func_compile,_version)
|
||||
target_link_options(funcfiftlib PRIVATE -sEXPORT_NAME=CompilerModule)
|
||||
target_link_options(funcfiftlib PRIVATE -sERROR_ON_UNDEFINED_SYMBOLS=0)
|
||||
target_link_options(funcfiftlib PRIVATE -sFILESYSTEM=1)
|
||||
target_link_options(funcfiftlib PRIVATE -Oz)
|
||||
target_link_options(funcfiftlib PRIVATE -sIGNORE_MISSING_MAIN=1)
|
||||
target_link_options(funcfiftlib PRIVATE -sAUTO_NATIVE_LIBRARIES=0)
|
||||
target_link_options(funcfiftlib PRIVATE -sMODULARIZE=1)
|
||||
target_link_options(funcfiftlib PRIVATE --embed-file ${CMAKE_CURRENT_SOURCE_DIR}/fift/lib@/fiftlib)
|
||||
target_compile_options(funcfiftlib PRIVATE -sDISABLE_EXCEPTION_CATCHING=0)
|
||||
endif()
|
||||
|
||||
add_executable(tlbc tl/tlbc.cpp)
|
||||
target_include_directories(tlbc PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
target_link_libraries(tlbc PUBLIC ton_crypto src_parser)
|
||||
|
@ -337,16 +357,26 @@ if (TON_USE_ASAN AND NOT WIN32)
|
|||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY smartcont/auto)
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
if (NOT CMAKE_CROSSCOMPILING OR USE_EMSCRIPTEN)
|
||||
set(GENERATE_TLB_CMD tlbc)
|
||||
add_custom_command(
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
||||
COMMAND ${TURN_OFF_LSAN}
|
||||
COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb
|
||||
COMMENT "Generate block tlb source files"
|
||||
OUTPUT ${TLB_BLOCK_AUTO}
|
||||
DEPENDS tlbc block/block.tlb
|
||||
)
|
||||
if (NOT USE_EMSCRIPTEN)
|
||||
add_custom_command(
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
||||
COMMAND ${TURN_OFF_LSAN}
|
||||
COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb
|
||||
COMMENT "Generate block tlb source files"
|
||||
OUTPUT ${TLB_BLOCK_AUTO}
|
||||
DEPENDS tlbc block/block.tlb
|
||||
)
|
||||
else()
|
||||
add_custom_command(
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
||||
COMMAND ${TURN_OFF_LSAN}
|
||||
COMMENT "Generate block tlb source files"
|
||||
OUTPUT ${TLB_BLOCK_AUTO}
|
||||
DEPENDS tlbc block/block.tlb
|
||||
)
|
||||
endif()
|
||||
add_custom_target(tlb_generate_block DEPENDS ${TLB_BLOCK_AUTO})
|
||||
add_dependencies(ton_block tlb_generate_block)
|
||||
|
||||
|
|
|
@ -982,7 +982,7 @@ struct ShardIdent::Record {
|
|||
int shard_pfx_bits;
|
||||
int workchain_id;
|
||||
unsigned long long shard_prefix;
|
||||
Record() : shard_pfx_bits(-1) {
|
||||
Record() : shard_pfx_bits(-1), workchain_id(ton::workchainInvalid), shard_prefix(0) {
|
||||
}
|
||||
Record(int _pfxlen, int _wcid, unsigned long long _pfx)
|
||||
: shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) {
|
||||
|
|
|
@ -451,7 +451,7 @@ block_extra in_msg_descr:^InMsgDescr
|
|||
created_by:bits256
|
||||
custom:(Maybe ^McBlockExtra) = BlockExtra;
|
||||
//
|
||||
value_flow ^[ from_prev_blk:CurrencyCollection
|
||||
value_flow#b8e48dfb ^[ from_prev_blk:CurrencyCollection
|
||||
to_next_blk:CurrencyCollection
|
||||
imported:CurrencyCollection
|
||||
exported:CurrencyCollection ]
|
||||
|
|
|
@ -327,8 +327,7 @@ bool Account::unpack(Ref<vm::CellSlice> shard_account, Ref<vm::CellSlice> extra,
|
|||
block::gen::t_ShardAccount.print(std::cerr, *shard_account);
|
||||
}
|
||||
block::gen::ShardAccount::Record acc_info;
|
||||
if (!(block::gen::t_ShardAccount.validate_csr(shard_account) &&
|
||||
block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
||||
if (!(block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
||||
LOG(ERROR) << "account " << addr.to_hex() << " state is invalid";
|
||||
return false;
|
||||
}
|
||||
|
@ -2013,7 +2012,6 @@ bool Transaction::compute_state() {
|
|||
std::cerr << "new account state: ";
|
||||
block::gen::t_Account.print_ref(std::cerr, new_total_state);
|
||||
}
|
||||
CHECK(block::gen::t_Account.validate_ref(new_total_state));
|
||||
CHECK(block::tlb::t_Account.validate_ref(new_total_state));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@ class AnyIntView {
|
|||
public:
|
||||
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift };
|
||||
typedef typename Tr::word_t word_t;
|
||||
typedef typename Tr::uword_t uword_t;
|
||||
int& n_;
|
||||
PropagateConstSpan<word_t> digits;
|
||||
|
||||
|
@ -320,7 +321,7 @@ class BigIntG {
|
|||
digits[0] = x;
|
||||
normalize_bool();
|
||||
} else {
|
||||
digits[0] = ((x + Tr::Half) & (Tr::Base - 1)) - Tr::Half;
|
||||
digits[0] = ((x ^ Tr::Half) & (Tr::Base - 1)) - Tr::Half;
|
||||
digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0);
|
||||
}
|
||||
}
|
||||
|
@ -675,7 +676,7 @@ class BigIntG {
|
|||
return n > 0 && !(digits[0] & 1);
|
||||
}
|
||||
word_t mod_pow2_short(int pow) const {
|
||||
return n > 0 ? digits[0] & ((1 << pow) - 1) : 0;
|
||||
return n > 0 ? digits[0] & ((1ULL << pow) - 1) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -764,7 +765,7 @@ bool AnyIntView<Tr>::add_pow2_any(int exponent, int factor) {
|
|||
while (size() <= k) {
|
||||
digits[inc_size()] = 0;
|
||||
}
|
||||
digits[k] += ((word_t)factor << dm.rem);
|
||||
digits[k] += factor * ((word_t)1 << dm.rem);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -969,7 +970,7 @@ bool AnyIntView<Tr>::add_mul_any(const AnyIntView<Tr>& yp, const AnyIntView<Tr>&
|
|||
if (hi && hi != -1) {
|
||||
return invalidate_bool();
|
||||
}
|
||||
digits[size() - 1] += (hi << word_shift);
|
||||
digits[size() - 1] += ((uword_t)hi << word_shift);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1014,7 +1015,7 @@ int AnyIntView<Tr>::sgn_un_any() const {
|
|||
}
|
||||
int i = size() - 2;
|
||||
do {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = digits[i];
|
||||
if (w >= -v + Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1059,7 +1060,7 @@ typename Tr::word_t AnyIntView<Tr>::to_long_any() const {
|
|||
} else if (size() == 1) {
|
||||
return digits[0];
|
||||
} else {
|
||||
word_t v = digits[0] + (digits[1] << word_shift); // approximation mod 2^64
|
||||
word_t v = (uword_t)digits[0] + ((uword_t)digits[1] << word_shift); // approximation mod 2^64
|
||||
word_t w = (v & (Tr::Base - 1)) - digits[0];
|
||||
w >>= word_shift;
|
||||
w += (v >> word_shift); // excess of approximation divided by Tr::Base
|
||||
|
@ -1120,7 +1121,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
|||
return -1;
|
||||
}
|
||||
while (xn > yn) {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = T::eval(digits[--xn]);
|
||||
if (w >= -v + Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1137,7 +1138,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
|||
return -1;
|
||||
}
|
||||
while (yn > xn) {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = yp.digits[--yn];
|
||||
if (w <= v - Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1150,7 +1151,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
|||
v = 0;
|
||||
}
|
||||
while (--xn >= 0) {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = T::eval(digits[xn]) - yp.digits[xn];
|
||||
if (w >= -v + Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1197,7 +1198,7 @@ int AnyIntView<Tr>::divmod_tiny_any(int y) {
|
|||
}
|
||||
int rem = 0;
|
||||
for (int i = size() - 1; i >= 0; i--) {
|
||||
auto divmod = std::div(digits[i] + ((word_t)rem << word_shift), (word_t)y);
|
||||
auto divmod = std::div(digits[i] + ((uword_t)rem << word_shift), (word_t)y);
|
||||
digits[i] = divmod.quot;
|
||||
rem = (int)divmod.rem;
|
||||
if ((rem ^ y) < 0 && rem) {
|
||||
|
@ -1267,7 +1268,7 @@ bool AnyIntView<Tr>::mul_add_short_any(word_t y, word_t z) {
|
|||
z += (digits[size() - 1] >> word_shift);
|
||||
digits[size() - 1] &= Tr::Base - 1;
|
||||
if (!z || z == -1) {
|
||||
digits[size() - 1] += (z << word_shift);
|
||||
digits[size() - 1] += ((uword_t)z << word_shift);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -1338,7 +1339,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
|
|||
while (--i >= 0) {
|
||||
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
|
||||
}
|
||||
digits[size() - 1] += (hi << word_shift);
|
||||
digits[size() - 1] += ((uword_t)hi << word_shift);
|
||||
}
|
||||
} else {
|
||||
quot.set_size(1);
|
||||
|
@ -1351,7 +1352,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
|
|||
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
|
||||
}
|
||||
dec_size();
|
||||
digits[size() - 1] += (digits[size()] << word_shift);
|
||||
digits[size() - 1] += ((uword_t)digits[size()] << word_shift);
|
||||
}
|
||||
if (size() >= yp.size() - 1) {
|
||||
assert(size() <= yp.size());
|
||||
|
@ -1455,7 +1456,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
|
|||
dec_size();
|
||||
q += word_shift;
|
||||
}
|
||||
word_t pow = ((word_t)1 << q);
|
||||
uword_t pow = ((uword_t)1 << q);
|
||||
word_t v = digits[size() - 1] & (pow - 1);
|
||||
if (!v) {
|
||||
int k = size() - 1;
|
||||
|
@ -1485,7 +1486,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
|
|||
return true;
|
||||
} else if (v >= Tr::Half && size() < max_size()) {
|
||||
word_t w = (((v >> (word_shift - 1)) + 1) >> 1);
|
||||
digits[size() - 1] = v - (w << word_shift);
|
||||
digits[size() - 1] = (uword_t)v - ((uword_t)w << word_shift);
|
||||
digits[inc_size()] = w;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -1623,7 +1624,7 @@ bool AnyIntView<Tr>::lshift_any(int exponent) {
|
|||
} else if (v != -1) {
|
||||
return invalidate_bool();
|
||||
} else {
|
||||
digits[size() - 1] += (v << word_shift);
|
||||
digits[size() - 1] += ((uword_t)v << word_shift);
|
||||
}
|
||||
}
|
||||
if (q) {
|
||||
|
@ -1750,7 +1751,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
int k = size() - 1;
|
||||
word_t q = digits[k];
|
||||
if (k > 0 && q < Tr::MaxDenorm / 2) {
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
if (!k) {
|
||||
|
@ -1766,7 +1767,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
} else if (q <= -Tr::MaxDenorm / 2) {
|
||||
return s;
|
||||
}
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
return q >= 0 ? s + 1 : s;
|
||||
|
@ -1774,7 +1775,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
int k = size() - 1;
|
||||
word_t q = digits[k];
|
||||
if (k > 0 && q > -Tr::MaxDenorm / 2) {
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
if (!k) {
|
||||
|
@ -1790,7 +1791,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
} else if (q <= -Tr::MaxDenorm / 2) {
|
||||
return s + 1;
|
||||
}
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
return q >= 0 ? s : s + 1;
|
||||
|
@ -1817,7 +1818,7 @@ bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size
|
|||
for (int i = 0; i < size(); i++) {
|
||||
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||
int k1 = 8 - k;
|
||||
v += (digits[i] << k) & 0xff;
|
||||
v += ((uword_t)digits[i] << k) & 0xff;
|
||||
if (ptr > buff) {
|
||||
*--ptr = (unsigned char)(v & 0xff);
|
||||
} else if ((unsigned char)(v & 0xff) != s) {
|
||||
|
@ -1827,7 +1828,7 @@ bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size
|
|||
v += (digits[i] >> k1);
|
||||
k += word_shift - 8;
|
||||
} else {
|
||||
v += (digits[i] << k);
|
||||
v += ((uword_t)digits[i] << k);
|
||||
k += word_shift;
|
||||
}
|
||||
while (k >= 8) {
|
||||
|
@ -1868,7 +1869,7 @@ bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_
|
|||
for (int i = 0; i < size(); i++) {
|
||||
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||
int k1 = 8 - k;
|
||||
v += (digits[i] << k) & 0xff;
|
||||
v += ((uword_t)digits[i] << k) & 0xff;
|
||||
if (buff < end) {
|
||||
*buff++ = (unsigned char)(v & 0xff);
|
||||
} else if ((unsigned char)(v & 0xff) != s) {
|
||||
|
@ -1878,7 +1879,7 @@ bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_
|
|||
v += (digits[i] >> k1);
|
||||
k += word_shift - 8;
|
||||
} else {
|
||||
v += (digits[i] << k);
|
||||
v += ((uword_t)digits[i] << k);
|
||||
k += word_shift;
|
||||
}
|
||||
while (k >= 8) {
|
||||
|
@ -1922,7 +1923,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
|||
return false;
|
||||
}
|
||||
}
|
||||
td::bitstring::bits_store_long_top(buff, offs, v << (64 - bits), bits);
|
||||
td::bitstring::bits_store_long_top(buff, offs, (unsigned long long)v << (64 - bits), bits);
|
||||
} else {
|
||||
if (!sgnd && v < 0) {
|
||||
return false;
|
||||
|
@ -1945,7 +1946,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
|||
for (int i = 0; i < size(); i++) {
|
||||
if (word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||
int k1 = 8 - k;
|
||||
v += (digits[i] << k) & 0xff;
|
||||
v += ((uword_t)digits[i] << k) & 0xff;
|
||||
if (ptr > buff) {
|
||||
if (--ptr > buff) {
|
||||
*ptr = (unsigned char)(v & 0xff);
|
||||
|
@ -1963,7 +1964,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
|||
v += (digits[i] >> k1);
|
||||
k += word_shift - 8;
|
||||
} else {
|
||||
v += (digits[i] << k);
|
||||
v += ((uword_t)digits[i] << k);
|
||||
k += word_shift;
|
||||
}
|
||||
while (k >= 8) {
|
||||
|
@ -2028,7 +2029,7 @@ bool AnyIntView<Tr>::import_bytes_any(const unsigned char* buff, std::size_t buf
|
|||
return invalidate_bool();
|
||||
}
|
||||
}
|
||||
v |= (((word_t) * --ptr) << k);
|
||||
v |= (((uword_t) * --ptr) << k);
|
||||
k += 8;
|
||||
}
|
||||
if (s) {
|
||||
|
@ -2043,7 +2044,9 @@ bool AnyIntView<Tr>::import_bits_any(const unsigned char* buff, int offs, unsign
|
|||
if (bits < word_shift) {
|
||||
set_size(1);
|
||||
unsigned long long val = td::bitstring::bits_load_long_top(buff, offs, bits);
|
||||
if (sgnd) {
|
||||
if (bits == 0) {
|
||||
digits[0] = 0;
|
||||
} else if (sgnd) {
|
||||
digits[0] = ((long long)val >> (64 - bits));
|
||||
} else {
|
||||
digits[0] = (val >> (64 - bits));
|
||||
|
|
|
@ -191,7 +191,7 @@ void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int
|
|||
*to++ = (unsigned char)(acc >> b);
|
||||
}
|
||||
if (b > 0) {
|
||||
*to = (unsigned char)((*to & (0xff >> b)) | ((int)acc << (8 - b)));
|
||||
*to = (unsigned char)((*to & (0xff >> b)) | ((unsigned)acc << (8 - b)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
|
|||
ptr++;
|
||||
}
|
||||
while (rem >= 8 && !td::is_aligned_pointer<8>(ptr)) {
|
||||
v = ((*ptr++ ^ xor_val) << 24);
|
||||
v = ((unsigned)(*ptr++ ^ xor_val) << 24);
|
||||
// std::cerr << "[B] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
|
||||
if (v) {
|
||||
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
|
||||
|
@ -319,7 +319,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
|
|||
rem -= 64;
|
||||
}
|
||||
while (rem >= 8) {
|
||||
v = ((*ptr++ ^ xor_val) << 24);
|
||||
v = ((unsigned)(*ptr++ ^ xor_val) << 24);
|
||||
// std::cerr << "[D] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
|
||||
if (v) {
|
||||
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
|
||||
|
@ -327,7 +327,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
|
|||
rem -= 8;
|
||||
}
|
||||
if (rem > 0) {
|
||||
v = ((*ptr ^ xor_val) << 24);
|
||||
v = ((unsigned)(*ptr ^ xor_val) << 24);
|
||||
// std::cerr << "[E] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
|
||||
c = td::count_leading_zeroes32(v);
|
||||
return c < rem ? bit_count - rem + c : bit_count;
|
||||
|
@ -505,7 +505,7 @@ unsigned long long bits_load_long_top(ConstBitPtr from, unsigned top_bits) {
|
|||
}
|
||||
|
||||
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits) {
|
||||
return bits_load_long_top(from, bits) >> (64 - bits);
|
||||
return bits == 0 ? 0 : bits_load_long_top(from, bits) >> (64 - bits);
|
||||
}
|
||||
|
||||
long long bits_load_long(ConstBitPtr from, unsigned bits) {
|
||||
|
|
|
@ -268,3 +268,114 @@ def? config-valid? {
|
|||
config-valid?
|
||||
} cond } cond } cond
|
||||
} : is-valid-config?
|
||||
|
||||
|
||||
// Get anycast depth / rewrite_pfx or return 0
|
||||
// ( S -- x y S )
|
||||
{
|
||||
// maybe
|
||||
1 u@+ swap 0 >
|
||||
{
|
||||
// anycast_info$_ depth:(#<= 30) { depth >= 1 }
|
||||
// rewrite_pfx:(bits depth) = Anycast;
|
||||
30 u@+ swap // get depth
|
||||
|
||||
dup 1 > {
|
||||
dup 2 roll swap u@+ // get rewrite_pfx
|
||||
// return depth, rewrite_pfx, slice
|
||||
}
|
||||
{
|
||||
drop // drop depth (<=1)
|
||||
0 0 2 roll // set anycast to none
|
||||
} cond
|
||||
}
|
||||
{
|
||||
0 0 2 roll // set anycast to none
|
||||
} cond
|
||||
} : maybe-anycast
|
||||
|
||||
// Rewrite first bits of addr with anycast info
|
||||
{ // input: anycast depth, rewrite_pfx, workchain, slice, address length
|
||||
4 -roll
|
||||
3 roll dup dup 0 = { 2drop 2 roll drop }
|
||||
{
|
||||
rot swap u@+ swap drop
|
||||
3 roll
|
||||
<b swap 3 roll u, b> <s swap |+
|
||||
} cond // rewrite first bits of address with rewrite_pfx
|
||||
2 roll
|
||||
u@+ // get address
|
||||
} : parse-address-with-anycast
|
||||
|
||||
// Parse Slice S and return:
|
||||
// 0 `addr_none S - if addr_none$00 is parsed
|
||||
// addr `addr_extern S - if addr_extern$01 is parsed
|
||||
// wc addr `addr_std S - if addr_std$10 is parsed
|
||||
// wc addr `addr_var S - if addr_var$11 is parsed
|
||||
// ( S -- 0 A S or addr A S or wc addr A S )
|
||||
{ 2 u@+ swap dup 0> // Get addr: addr_none$00 / addr_extern$01 / addr_std$10 / addr_var$11
|
||||
{ // if greater that zero
|
||||
dup 1 >
|
||||
{
|
||||
2 =
|
||||
{
|
||||
// if addr_std$10
|
||||
// anycast:(Maybe Anycast)
|
||||
// workchain_id:int8
|
||||
// address:bits256 = MsgAddressInt;
|
||||
maybe-anycast // get anycast depth, bits, slice
|
||||
8 i@+ // get workchain
|
||||
256 parse-address-with-anycast
|
||||
`addr-std swap
|
||||
}
|
||||
|
||||
{
|
||||
// if addr_var$11
|
||||
// anycast:(Maybe Anycast)
|
||||
// addr_len:(## 9)
|
||||
// workchain_id:int32
|
||||
// address:(bits addr_len) = MsgAddressInt;
|
||||
maybe-anycast // get anycast depth, bits, slice
|
||||
9 u@+ // get addr_len
|
||||
32 i@+ // get workchain
|
||||
swap 2 -roll // move workchain to neede position
|
||||
swap parse-address-with-anycast
|
||||
`addr-var swap
|
||||
} cond
|
||||
|
||||
}
|
||||
{
|
||||
drop // drop header (dup for statment upper)
|
||||
// if addr_extern$01
|
||||
// addr_extern$01 len:(## 9)
|
||||
// external_address:(bits len)
|
||||
9 u@+ swap // bit len
|
||||
u@+ // external_address
|
||||
`addr-extern swap
|
||||
} cond
|
||||
}
|
||||
{
|
||||
swap
|
||||
// if addr_none$00
|
||||
`addr-none swap
|
||||
} cond
|
||||
} : addr@+
|
||||
|
||||
{ addr@+ drop } : addr@
|
||||
|
||||
// User-friendly prints output of addr@
|
||||
// (0 A or addr A or wc addr A -- )
|
||||
{
|
||||
dup `addr-none eq?
|
||||
{ 2drop ."addr_none" }
|
||||
{
|
||||
`addr-extern eq?
|
||||
{ (dump) type }
|
||||
{ (x.) swap (dump) ":" $+ swap $+ type }
|
||||
cond
|
||||
}
|
||||
cond
|
||||
} : print-addr // print addr with workchain
|
||||
|
||||
forget maybe-anycast
|
||||
forget parse-address-with-anycast
|
||||
|
|
91
crypto/func/auto-tests/run_tests.py
Normal file
91
crypto/func/auto-tests/run_tests.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
def getenv(name, default=None):
|
||||
if name in os.environ:
|
||||
return os.environ[name]
|
||||
if default is None:
|
||||
print("Environment variable", name, "is not set", file=sys.stderr)
|
||||
exit(1)
|
||||
return default
|
||||
|
||||
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
|
||||
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
|
||||
#FUNC_STDLIB = getenv("FUNC_STDLIB")
|
||||
FIFT_LIBS = getenv("FIFT_LIBS")
|
||||
TMP_DIR = tempfile.mkdtemp()
|
||||
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
|
||||
RUNNER_FIF = os.path.join(TMP_DIR, "runner.fif")
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage : run_tests.py tests_dir", file=sys.stderr)
|
||||
exit(1)
|
||||
TESTS_DIR = sys.argv[1]
|
||||
|
||||
class ExecutionError(Exception):
|
||||
pass
|
||||
|
||||
def compile_func(f):
|
||||
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
|
||||
if res.returncode != 0:
|
||||
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||
|
||||
def run_runner():
|
||||
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, RUNNER_FIF], capture_output=True, timeout=10)
|
||||
if res.returncode != 0:
|
||||
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||
s = str(res.stdout, "utf-8")
|
||||
s = [x.strip() for x in s.split("\n")]
|
||||
return [x for x in s if x != ""]
|
||||
|
||||
tests = [s for s in os.listdir(TESTS_DIR) if s.endswith(".fc")]
|
||||
tests.sort()
|
||||
print("Found", len(tests), "tests", file=sys.stderr)
|
||||
for ti, tf in enumerate(tests):
|
||||
print("Running test %d/%d: %s" % (ti + 1, len(tests), tf), file=sys.stderr)
|
||||
tf = os.path.join(TESTS_DIR, tf)
|
||||
try:
|
||||
compile_func(tf)
|
||||
except ExecutionError as e:
|
||||
print(file=sys.stderr)
|
||||
print("Compilation error", file=sys.stderr)
|
||||
print(e, file=sys.stderr)
|
||||
exit(2)
|
||||
with open(tf, "r") as fd:
|
||||
lines = fd.readlines()
|
||||
cases = []
|
||||
for s in lines:
|
||||
s = [x.strip() for x in s.split("|")]
|
||||
if len(s) == 4 and s[0].strip() == "TESTCASE":
|
||||
cases.append(s[1:])
|
||||
if len(cases) == 0:
|
||||
print(file=sys.stderr)
|
||||
print("Error: no test cases", file=sys.stderr)
|
||||
exit(2)
|
||||
|
||||
with open(RUNNER_FIF, "w") as f:
|
||||
print("\"%s\" include <s constant code" % COMPILED_FIF, file=f)
|
||||
for function, test_in, _ in cases:
|
||||
print(test_in, function, "code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times", file=f)
|
||||
try:
|
||||
func_out = run_runner()
|
||||
if len(func_out) != len(cases):
|
||||
raise ExecutionError("Unexpected number of lines")
|
||||
for i in range(len(func_out)):
|
||||
if func_out[i] != cases[i][2]:
|
||||
raise ExecutionError("Error on case %d: expected '%s', found '%s'" % (i + 1, cases[i][2], func_out[i]))
|
||||
except ExecutionError as e:
|
||||
print(file=sys.stderr)
|
||||
print("Error:", file=sys.stderr)
|
||||
print(e, file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
print("Compiled:", file=sys.stderr)
|
||||
with open(COMPILED_FIF, "r") as f:
|
||||
print(f.read(), file=sys.stderr)
|
||||
exit(2)
|
||||
print(" OK, %d cases" % len(cases), file=sys.stderr)
|
||||
|
||||
print("Done", file=sys.stderr)
|
239
crypto/func/auto-tests/stress_tester.py
Normal file
239
crypto/func/auto-tests/stress_tester.py
Normal file
|
@ -0,0 +1,239 @@
|
|||
import os
|
||||
import os.path
|
||||
import random
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
def getenv(name, default=None):
|
||||
if name in os.environ:
|
||||
return os.environ[name]
|
||||
if default is not None:
|
||||
return default
|
||||
print("Environemnt variable", name, "is not set", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
VAR_CNT = 5
|
||||
TMP_DIR = tempfile.mkdtemp()
|
||||
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
|
||||
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
|
||||
FIFT_LIBS = getenv("FIFT_LIBS")
|
||||
MAGIC = 123456789
|
||||
|
||||
var_idx = 0
|
||||
def gen_var_name():
|
||||
global var_idx
|
||||
var_idx += 1
|
||||
return "i%d" % var_idx
|
||||
|
||||
class State:
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
self.vs = [0] * VAR_CNT
|
||||
|
||||
class Code:
|
||||
pass
|
||||
|
||||
class CodeEmpty(Code):
|
||||
def execute(self, state):
|
||||
return None
|
||||
|
||||
def write(self, f, indent=0):
|
||||
pass
|
||||
|
||||
class CodeReturn(Code):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def execute(self, state):
|
||||
return [self.value] + state.vs
|
||||
|
||||
def write(self, f, indent=0):
|
||||
print(" " * indent + "return (%d, %s);" % (self.value, ", ".join("v%d" % i for i in range(VAR_CNT))), file=f)
|
||||
|
||||
class CodeAdd(Code):
|
||||
def __init__(self, i, value):
|
||||
self.i = i
|
||||
self.value = value
|
||||
|
||||
def execute(self, state):
|
||||
state.vs[self.i] += self.value
|
||||
return None
|
||||
|
||||
def write(self, f, indent=0):
|
||||
print(" " * indent + "v%d += %d;" % (self.i, self.value), file=f)
|
||||
|
||||
class CodeBlock(Code):
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
|
||||
def execute(self, state):
|
||||
for c in self.code:
|
||||
res = c.execute(state)
|
||||
if res is not None:
|
||||
return res
|
||||
return None
|
||||
|
||||
def write(self, f, indent=0):
|
||||
for c in self.code:
|
||||
c.write(f, indent)
|
||||
|
||||
class CodeIfRange(Code):
|
||||
def __init__(self, l, r, c1, c2):
|
||||
self.l = l
|
||||
self.r = r
|
||||
self.c1 = c1
|
||||
self.c2 = c2
|
||||
|
||||
def execute(self, state):
|
||||
if self.l <= state.x < self.r:
|
||||
return self.c1.execute(state)
|
||||
else:
|
||||
return self.c2.execute(state)
|
||||
|
||||
def write(self, f, indent=0):
|
||||
print(" " * indent + "if (in(x, %d, %d)) {" % (self.l, self.r), file=f)
|
||||
self.c1.write(f, indent + 1)
|
||||
if isinstance(self.c2, CodeEmpty):
|
||||
print(" " * indent + "}", file=f)
|
||||
else:
|
||||
print(" " * indent + "} else {", file=f)
|
||||
self.c2.write(f, indent + 1)
|
||||
print(" " * indent + "}", file=f)
|
||||
|
||||
class CodeRepeat(Code):
|
||||
def __init__(self, n, c, loop_type):
|
||||
if loop_type == 2:
|
||||
n = max(n, 1)
|
||||
self.n = n
|
||||
self.c = c
|
||||
self.loop_type = loop_type
|
||||
|
||||
def execute(self, state):
|
||||
for _ in range(self.n):
|
||||
res = self.c.execute(state)
|
||||
if res is not None:
|
||||
return res
|
||||
return None
|
||||
|
||||
def write(self, f, indent=0):
|
||||
if self.loop_type == 0:
|
||||
print(" " * indent + "repeat (%d) {" % self.n, file=f)
|
||||
self.c.write(f, indent + 1)
|
||||
print(" " * indent + "}", file=f)
|
||||
elif self.loop_type == 1:
|
||||
var = gen_var_name()
|
||||
print(" " * indent + "int %s = 0;" % var, file=f)
|
||||
print(" " * indent + "while (%s < %d) {" % (var, self.n), file=f)
|
||||
self.c.write(f, indent + 1)
|
||||
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
||||
print(" " * indent + "}", file=f)
|
||||
else:
|
||||
var = gen_var_name()
|
||||
print(" " * indent + "int %s = 0;" % var, file=f)
|
||||
print(" " * indent + "do {", file=f)
|
||||
self.c.write(f, indent + 1)
|
||||
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
||||
print(" " * indent + "} until (%s >= %d);" % (var, self.n), file=f)
|
||||
|
||||
def write_function(f, name, body, inline=False, inline_ref=False, method_id=None):
|
||||
print("_ %s(int x)" % name, file=f, end="")
|
||||
if inline:
|
||||
print(" inline", file=f, end="")
|
||||
if inline_ref:
|
||||
print(" inline_ref", file=f, end="")
|
||||
if method_id is not None:
|
||||
print(" method_id(%d)" % method_id, file=f, end="")
|
||||
print(" {", file=f)
|
||||
for i in range(VAR_CNT):
|
||||
print(" int v%d = 0;" % i, file=f)
|
||||
body.write(f, 1);
|
||||
print("}", file=f)
|
||||
|
||||
def gen_code(xl, xr, with_return, loop_depth=0):
|
||||
code = []
|
||||
for _ in range(random.randint(0, 2)):
|
||||
if random.randint(0, 3) == 0 and loop_depth < 3:
|
||||
c = gen_code(xl, xr, False, loop_depth + 1)
|
||||
code.append(CodeRepeat(random.randint(0, 3), c, random.randint(0, 2)))
|
||||
elif xr - xl > 1:
|
||||
xmid = random.randrange(xl + 1, xr)
|
||||
ret = random.choice((0, 0, 0, 0, 0, 1, 2))
|
||||
c1 = gen_code(xl, xmid, ret == 1, loop_depth)
|
||||
if random.randrange(5) == 0:
|
||||
c2 = CodeEmpty()
|
||||
else:
|
||||
c2 = gen_code(xmid, xr, ret == 2, loop_depth)
|
||||
code.append(CodeIfRange(xl, xmid, c1, c2))
|
||||
if with_return:
|
||||
if xr - xl == 1:
|
||||
code.append(CodeReturn(random.randrange(10**9)))
|
||||
else:
|
||||
xmid = random.randrange(xl + 1, xr)
|
||||
c1 = gen_code(xl, xmid, True, loop_depth)
|
||||
c2 = gen_code(xmid, xr, True, loop_depth)
|
||||
code.append(CodeIfRange(xl, xmid, c1, c2))
|
||||
for _ in range(random.randint(0, 3)):
|
||||
pos = random.randint(0, len(code))
|
||||
code.insert(pos, CodeAdd(random.randrange(VAR_CNT), random.randint(0, 10**6)))
|
||||
if len(code) == 0:
|
||||
return CodeEmpty()
|
||||
return CodeBlock(code)
|
||||
|
||||
class ExecutionError(Exception):
|
||||
pass
|
||||
|
||||
def compile_func(fc, fif):
|
||||
res = subprocess.run([FUNC_EXECUTABLE, "-o", fif, "-SPA", fc], capture_output=True)
|
||||
if res.returncode != 0:
|
||||
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||
|
||||
def runvm(compiled_fif, xl, xr):
|
||||
runner = os.path.join(TMP_DIR, "runner.fif")
|
||||
with open(runner, "w") as f:
|
||||
print("\"%s\" include <s constant code" % compiled_fif, file=f)
|
||||
for x in range(xl, xr):
|
||||
print("%d 0 code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times" % x, file=f)
|
||||
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, runner], capture_output=True)
|
||||
if res.returncode != 0:
|
||||
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||
output = []
|
||||
for s in str(res.stdout, "utf-8").split("\n"):
|
||||
if s.strip() != "":
|
||||
output.append(list(map(int, s.split())))
|
||||
return output
|
||||
|
||||
cnt_ok = 0
|
||||
cnt_fail = 0
|
||||
for test_id in range(0, 1000000):
|
||||
random.seed(test_id)
|
||||
inline = random.randint(0, 2)
|
||||
xr = random.randint(1, 15)
|
||||
var_idx = 0
|
||||
code = gen_code(0, xr, True)
|
||||
fc = os.path.join(TMP_DIR, "code.fc")
|
||||
fif = os.path.join(TMP_DIR, "compiled.fif")
|
||||
with open(fc, "w") as f:
|
||||
print("int in(int x, int l, int r) impure { return (l <= x) & (x < r); }", file=f)
|
||||
write_function(f, "foo", code, inline=(inline == 1), inline_ref=(inline == 2))
|
||||
print("_ main(int x) {", file=f)
|
||||
print(" (int ret, %s) = foo(x);" % ", ".join("int v%d" % i for i in range(VAR_CNT)), file=f)
|
||||
print(" return (ret, %s, %d);" % (", ".join("v%d" % i for i in range(VAR_CNT)), MAGIC), file=f)
|
||||
print("}", file=f)
|
||||
compile_func(fc, fif)
|
||||
ok = True
|
||||
try:
|
||||
output = runvm(fif, 0, xr)
|
||||
for x in range(xr):
|
||||
my_out = code.execute(State(x)) + [MAGIC]
|
||||
fc_out = output[x]
|
||||
if my_out != fc_out:
|
||||
ok = False
|
||||
break
|
||||
except ExecutionError:
|
||||
ok = False
|
||||
if ok:
|
||||
cnt_ok += 1
|
||||
else:
|
||||
cnt_fail += 1
|
||||
print("Test %-6d %-6s ok:%-6d fail:%-6d" % (test_id, "OK" if ok else "FAIL", cnt_ok, cnt_fail), file=sys.stderr)
|
17
crypto/func/auto-tests/tests/a10.fc
Normal file
17
crypto/func/auto-tests/tests/a10.fc
Normal file
|
@ -0,0 +1,17 @@
|
|||
_ main(int a, int x) {
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
while ((y = x * x) > a) {
|
||||
x -= 1;
|
||||
z = 1;
|
||||
}
|
||||
return (y, z);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 101 15 | 100 1
|
||||
TESTCASE | 0 | 101 14 | 100 1
|
||||
TESTCASE | 0 | 101 10 | 100 0
|
||||
TESTCASE | 0 | 100 10 | 100 0
|
||||
-}
|
89
crypto/func/auto-tests/tests/a6.fc
Normal file
89
crypto/func/auto-tests/tests/a6.fc
Normal file
|
@ -0,0 +1,89 @@
|
|||
(int, int) f(int a, int b, int c, int d, int e, int f) {
|
||||
;; solve a 2x2 linear equation
|
||||
int D = a * d - b * c;
|
||||
int Dx = e * d - b * f;
|
||||
int Dy = a * f - e * c;
|
||||
return (Dx / D, Dy / D);
|
||||
}
|
||||
|
||||
int calc_phi() {
|
||||
var n = 1;
|
||||
repeat (70) { n *= 10; }
|
||||
var p = var q = 1;
|
||||
do {
|
||||
(p, q) = (q, p + q);
|
||||
} until (q > n);
|
||||
return muldivr(p, n, q);
|
||||
}
|
||||
|
||||
int calc_sqrt2() {
|
||||
var n = 1;
|
||||
repeat (70) { n *= 10; }
|
||||
var p = var q = 1;
|
||||
do {
|
||||
var t = p + q;
|
||||
(p, q) = (q, t + q);
|
||||
} until (q > n);
|
||||
return muldivr(p, n, q);
|
||||
}
|
||||
|
||||
var calc_root(m) {
|
||||
int base = 1;
|
||||
repeat(70) { base *= 10; }
|
||||
var (a, b, c) = (1, 0, - m);
|
||||
var (p1, q1, p2, q2) = (1, 0, 0, 1);
|
||||
do {
|
||||
int k = -1;
|
||||
var (a1, b1, c1) = (0, 0, 0);
|
||||
do {
|
||||
k += 1;
|
||||
(a1, b1, c1) = (a, b, c);
|
||||
c += b;
|
||||
c += b += a;
|
||||
} until (c > 0);
|
||||
(a, b, c) = (- c1, - b1, - a1);
|
||||
(p1, q1) = (k * p1 + q1, p1);
|
||||
(p2, q2) = (k * p2 + q2, p2);
|
||||
} until (p1 > base);
|
||||
return (p1, q1, p2, q2);
|
||||
}
|
||||
|
||||
{-
|
||||
operator _/%_ infix 20;
|
||||
|
||||
(int, int) ((int x) /% (int y)) {
|
||||
return (x / y, x % y);
|
||||
}
|
||||
|
||||
(int, int) _/%_ (int x, int y) {
|
||||
return (x / y, x % y);
|
||||
}
|
||||
-}
|
||||
|
||||
int ataninv(int base, int q) { ;; computes base*atan(1/q)
|
||||
base ~/= q;
|
||||
q *= - q;
|
||||
int sum = 0;
|
||||
int n = 1;
|
||||
do {
|
||||
sum += base ~/ n;
|
||||
base ~/= q;
|
||||
n += 2;
|
||||
} until base == 0;
|
||||
return sum;
|
||||
}
|
||||
|
||||
int calc_pi() {
|
||||
int base = 64;
|
||||
repeat (70) { base *= 10; }
|
||||
return (ataninv(base << 2, 5) - ataninv(base, 239)) ~>> 4;
|
||||
}
|
||||
|
||||
int main() {
|
||||
return calc_pi();
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | | 31415926535897932384626433832795028841971693993751058209749445923078164
|
||||
-}
|
16
crypto/func/auto-tests/tests/a6_1.fc
Normal file
16
crypto/func/auto-tests/tests/a6_1.fc
Normal file
|
@ -0,0 +1,16 @@
|
|||
(int, int) main(int a, int b, int c, int d, int e, int f) {
|
||||
int D = a * d - b * c;
|
||||
int Dx = e * d - b * f;
|
||||
int Dy = a * f - e * c;
|
||||
return (Dx / D, Dy / D);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 1 1 1 -1 10 6 | 8 2
|
||||
TESTCASE | 0 | 817 -31 624 -241 132272 272276 | 132 -788
|
||||
TESTCASE | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
|
||||
TESTCASE | 0 | 448 -433 -444 792 150012 -356232 | -218 -572
|
||||
TESTCASE | 0 | -40 -821 433 -734 -721629 -741724 | -206 889
|
||||
TESTCASE | 0 | -261 -98 -494 868 -166153 733738 | 263 995
|
||||
-}
|
24
crypto/func/auto-tests/tests/a6_5.fc
Normal file
24
crypto/func/auto-tests/tests/a6_5.fc
Normal file
|
@ -0,0 +1,24 @@
|
|||
var twice(f, x) {
|
||||
return f (f x);
|
||||
}
|
||||
|
||||
_ sqr(x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
var main(x) {
|
||||
var f = sqr;
|
||||
return twice(f, x) * f(x);
|
||||
}
|
||||
|
||||
var pow6(x) method_id(4) {
|
||||
return twice(sqr, x) * sqr(x);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 3 | 729
|
||||
TESTCASE | 0 | 10 | 1000000
|
||||
TESTCASE | 4 | 3 | 729
|
||||
TESTCASE | 4 | 10 | 1000000
|
||||
-}
|
24
crypto/func/auto-tests/tests/a7.fc
Normal file
24
crypto/func/auto-tests/tests/a7.fc
Normal file
|
@ -0,0 +1,24 @@
|
|||
() main() { }
|
||||
int steps(int x) method_id(1) {
|
||||
var n = 0;
|
||||
while (x > 1) {
|
||||
n += 1;
|
||||
if (x & 1) {
|
||||
x = 3 * x + 1;
|
||||
} else {
|
||||
x >>= 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 1 | 1 | 0
|
||||
TESTCASE | 1 | 2 | 1
|
||||
TESTCASE | 1 | 5 | 5
|
||||
TESTCASE | 1 | 19 | 20
|
||||
TESTCASE | 1 | 27 | 111
|
||||
TESTCASE | 1 | 100 | 25
|
||||
-}
|
||||
|
17
crypto/func/auto-tests/tests/c2.fc
Normal file
17
crypto/func/auto-tests/tests/c2.fc
Normal file
|
@ -0,0 +1,17 @@
|
|||
global ((int, int) -> int) op;
|
||||
|
||||
int check_assoc(int a, int b, int c) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
int main(int x, int y, int z) {
|
||||
op = _+_;
|
||||
return check_assoc(x, y, z);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 2 3 9 | -1
|
||||
TESTCASE | 0 | 11 22 44 | -1
|
||||
TESTCASE | 0 | -1 -10 -20 | -1
|
||||
-}
|
14
crypto/func/auto-tests/tests/c2_1.fc
Normal file
14
crypto/func/auto-tests/tests/c2_1.fc
Normal file
|
@ -0,0 +1,14 @@
|
|||
_ check_assoc(op, a, b, c) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
int main(int x, int y, int z) {
|
||||
return check_assoc(_+_, x, y, z);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 2 3 9 | -1
|
||||
TESTCASE | 0 | 11 22 44 | -1
|
||||
TESTCASE | 0 | -1 -10 -20 | -1
|
||||
-}
|
60
crypto/func/auto-tests/tests/co1.fc
Normal file
60
crypto/func/auto-tests/tests/co1.fc
Normal file
|
@ -0,0 +1,60 @@
|
|||
const int1 = 1, int2 = 2;
|
||||
|
||||
const int int101 = 101;
|
||||
const int int111 = 111;
|
||||
|
||||
const int1r = int1;
|
||||
|
||||
const str1 = "const1", str2 = "aabbcc"s;
|
||||
|
||||
const slice str2r = str2;
|
||||
|
||||
const str1int = 0x636f6e737431;
|
||||
const str2int = 0xAABBCC;
|
||||
|
||||
const int nibbles = 4;
|
||||
|
||||
int iget1() { return int1; }
|
||||
int iget2() { return int2; }
|
||||
int iget3() { return int1 + int2; }
|
||||
|
||||
int iget1r() { return int1r; }
|
||||
|
||||
slice sget1() { return str1; }
|
||||
slice sget2() { return str2; }
|
||||
slice sget2r() { return str2r; }
|
||||
|
||||
const int int240 = ((int1 + int2) * 10) << 3;
|
||||
|
||||
int iget240() { return int240; }
|
||||
|
||||
builder newc() asm "NEWC";
|
||||
slice endcs(builder b) asm "ENDC" "CTOS";
|
||||
int sdeq (slice s1, slice s2) asm "SDEQ";
|
||||
builder stslicer(builder b, slice s) asm "STSLICER";
|
||||
|
||||
_ main() {
|
||||
int i1 = iget1();
|
||||
int i2 = iget2();
|
||||
int i3 = iget3();
|
||||
|
||||
throw_unless(int101, i1 == 1);
|
||||
throw_unless(102, i2 == 2);
|
||||
throw_unless(103, i3 == 3);
|
||||
|
||||
slice s1 = sget1();
|
||||
slice s2 = sget2();
|
||||
slice s3 = newc().stslicer(str1).stslicer(str2r).endcs();
|
||||
|
||||
throw_unless(int111, sdeq(s1, newc().store_uint(str1int, 12 * nibbles).endcs()));
|
||||
throw_unless(112, sdeq(s2, newc().store_uint(str2int, 6 * nibbles).endcs()));
|
||||
throw_unless(113, sdeq(s3, newc().store_uint(0x636f6e737431AABBCC, 18 * nibbles).endcs()));
|
||||
|
||||
int i4 = iget240();
|
||||
throw_unless(104, i4 == 240);
|
||||
return 0;
|
||||
}
|
||||
|
||||
{-
|
||||
TESTCASE | 0 | | 0
|
||||
-}
|
19
crypto/func/auto-tests/tests/code_after_ifelse.fc
Normal file
19
crypto/func/auto-tests/tests/code_after_ifelse.fc
Normal file
|
@ -0,0 +1,19 @@
|
|||
int foo(int x) inline method_id(1) {
|
||||
if (x == 1) {
|
||||
return 111;
|
||||
} else {
|
||||
x *= 2;
|
||||
}
|
||||
return x + 1;
|
||||
}
|
||||
(int, int) main(int x) {
|
||||
return (foo(x), 222);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 1 | 1 | 111
|
||||
TESTCASE | 1 | 3 | 7
|
||||
TESTCASE | 0 | 1 | 111 222
|
||||
TESTCASE | 0 | 3 | 7 222
|
||||
-}
|
61
crypto/func/auto-tests/tests/inline_big.fc
Normal file
61
crypto/func/auto-tests/tests/inline_big.fc
Normal file
|
@ -0,0 +1,61 @@
|
|||
int foo(int x) inline {
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
x = x * 10 + 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
(int) main(int x) {
|
||||
return foo(x) * 10 + 5;
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 9 | 9111111111111111111111111111111111111111111111111115
|
||||
-}
|
26
crypto/func/auto-tests/tests/inline_if.fc
Normal file
26
crypto/func/auto-tests/tests/inline_if.fc
Normal file
|
@ -0,0 +1,26 @@
|
|||
int foo1(int x) {
|
||||
if (x == 1) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
int foo2(int x) inline {
|
||||
if (x == 1) {
|
||||
return 11;
|
||||
}
|
||||
return 22;
|
||||
}
|
||||
int foo3(int x) inline_ref {
|
||||
if (x == 1) {
|
||||
return 111;
|
||||
}
|
||||
return 222;
|
||||
}
|
||||
(int, int, int) main(int x) {
|
||||
return (foo1(x) + 1, foo2(x) + 1, foo3(x) + 1);
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 1 | 2 12 112
|
||||
TESTCASE | 0 | 2 | 3 23 223
|
||||
-}
|
43
crypto/func/auto-tests/tests/inline_loops.fc
Normal file
43
crypto/func/auto-tests/tests/inline_loops.fc
Normal file
|
@ -0,0 +1,43 @@
|
|||
global int g;
|
||||
|
||||
_ foo_repeat() impure inline {
|
||||
g = 1;
|
||||
repeat(5) {
|
||||
g *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
int foo_until() impure inline {
|
||||
g = 1;
|
||||
int i = 0;
|
||||
do {
|
||||
g *= 2;
|
||||
i += 1;
|
||||
} until (i >= 8);
|
||||
return i;
|
||||
}
|
||||
|
||||
int foo_while() impure inline {
|
||||
g = 1;
|
||||
int i = 0;
|
||||
while (i < 10) {
|
||||
g *= 2;
|
||||
i += 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
_ main() {
|
||||
foo_repeat();
|
||||
int x = g;
|
||||
foo_until();
|
||||
int y = g;
|
||||
foo_while();
|
||||
int z = g;
|
||||
return (x, y, z);
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | | 32 256 1024
|
||||
-}
|
12
crypto/func/auto-tests/tests/method_id.fc
Normal file
12
crypto/func/auto-tests/tests/method_id.fc
Normal file
|
@ -0,0 +1,12 @@
|
|||
int foo1() method_id(1) { return 111; }
|
||||
int foo2() method_id(3) { return 222; }
|
||||
int foo3() method_id(10) { return 333; }
|
||||
int main() { return 999; }
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 1 | | 111
|
||||
TESTCASE | 3 | | 222
|
||||
TESTCASE | 10 | | 333
|
||||
TESTCASE | 0 | | 999
|
||||
-}
|
54
crypto/func/auto-tests/tests/s1.fc
Normal file
54
crypto/func/auto-tests/tests/s1.fc
Normal file
|
@ -0,0 +1,54 @@
|
|||
slice ascii_slice() method_id {
|
||||
return "string";
|
||||
}
|
||||
|
||||
slice raw_slice() method_id {
|
||||
return "abcdef"s;
|
||||
}
|
||||
|
||||
slice addr_slice() method_id {
|
||||
return "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a;
|
||||
}
|
||||
|
||||
int string_hex() method_id {
|
||||
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"u;
|
||||
}
|
||||
|
||||
int string_minihash() method_id {
|
||||
return "transfer(slice, int)"h;
|
||||
}
|
||||
|
||||
int string_maxihash() method_id {
|
||||
return "transfer(slice, int)"H;
|
||||
}
|
||||
|
||||
int string_crc32() method_id {
|
||||
return "transfer(slice, int)"c;
|
||||
}
|
||||
|
||||
builder newc() asm "NEWC";
|
||||
slice endcs(builder b) asm "ENDC" "CTOS";
|
||||
int sdeq (slice s1, slice s2) asm "SDEQ";
|
||||
|
||||
_ main() {
|
||||
slice s_ascii = ascii_slice();
|
||||
slice s_raw = raw_slice();
|
||||
slice s_addr = addr_slice();
|
||||
int i_hex = string_hex();
|
||||
int i_mini = string_minihash();
|
||||
int i_maxi = string_maxihash();
|
||||
int i_crc = string_crc32();
|
||||
throw_unless(101, sdeq(s_ascii, newc().store_uint(0x737472696E67, 12 * 4).endcs()));
|
||||
throw_unless(102, sdeq(s_raw, newc().store_uint(0xABCDEF, 6 * 4).endcs()));
|
||||
throw_unless(103, sdeq(s_addr, newc().store_uint(4, 3).store_int(-1, 8)
|
||||
.store_uint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs()));
|
||||
throw_unless(104, i_hex == 0x4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435);
|
||||
throw_unless(105, i_mini == 0x7a62e8a8);
|
||||
throw_unless(106, i_maxi == 0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979);
|
||||
throw_unless(107, i_crc == 2235694568);
|
||||
return 0;
|
||||
}
|
||||
|
||||
{-
|
||||
TESTCASE | 0 | | 0
|
||||
-}
|
17
crypto/func/auto-tests/tests/unbalanced_ret.fc
Normal file
17
crypto/func/auto-tests/tests/unbalanced_ret.fc
Normal file
|
@ -0,0 +1,17 @@
|
|||
(int, int) main(int x) {
|
||||
int y = 5;
|
||||
if (x < 0) {
|
||||
x *= 2;
|
||||
y += 1;
|
||||
if (x == -10) {
|
||||
return (111, 0);
|
||||
}
|
||||
}
|
||||
return (x + 1, y);
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 10 | 11 5
|
||||
TESTCASE | 0 | -5 | 111 0
|
||||
TESTCASE | 0 | -4 | -7 6
|
||||
-}
|
18
crypto/func/auto-tests/tests/unbalanced_ret_inline.fc
Normal file
18
crypto/func/auto-tests/tests/unbalanced_ret_inline.fc
Normal file
|
@ -0,0 +1,18 @@
|
|||
int foo(int x) inline {
|
||||
if (x < 0) {
|
||||
x *= 2;
|
||||
if (x == -10) {
|
||||
return 111;
|
||||
}
|
||||
}
|
||||
return x + 1;
|
||||
}
|
||||
int main(int x) {
|
||||
return foo(x) * 10;
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 10 | 110
|
||||
TESTCASE | 0 | -5 | 1110
|
||||
TESTCASE | 0 | -4 | -70
|
||||
-}
|
48
crypto/func/auto-tests/tests/unbalanced_ret_loops.fc
Normal file
48
crypto/func/auto-tests/tests/unbalanced_ret_loops.fc
Normal file
|
@ -0,0 +1,48 @@
|
|||
_ main() { }
|
||||
|
||||
int foo_repeat(int x) method_id(1) {
|
||||
repeat(10) {
|
||||
x += 10;
|
||||
if (x >= 100) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int foo_while(int x) method_id(2) {
|
||||
int i = 0;
|
||||
while (i < 10) {
|
||||
x += 10;
|
||||
if (x >= 100) {
|
||||
return x;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int foo_until(int x) method_id(3) {
|
||||
int i = 0;
|
||||
do {
|
||||
x += 10;
|
||||
if (x >= 100) {
|
||||
return x;
|
||||
}
|
||||
i += 1;
|
||||
} until (i >= 10);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 1 | 40 | 100
|
||||
TESTCASE | 1 | 33 | 103
|
||||
TESTCASE | 1 | -5 | -1
|
||||
TESTCASE | 2 | 40 | 100
|
||||
TESTCASE | 2 | 33 | 103
|
||||
TESTCASE | 2 | -5 | -1
|
||||
TESTCASE | 3 | 40 | 100
|
||||
TESTCASE | 3 | 33 | 103
|
||||
TESTCASE | 3 | -5 | -1
|
||||
-}
|
35
crypto/func/auto-tests/tests/unbalanced_ret_nested.fc
Normal file
35
crypto/func/auto-tests/tests/unbalanced_ret_nested.fc
Normal file
|
@ -0,0 +1,35 @@
|
|||
int foo(int y) {
|
||||
if (y < 0) {
|
||||
y *= 2;
|
||||
if (y == -10) {
|
||||
return 111;
|
||||
}
|
||||
}
|
||||
return y + 1;
|
||||
}
|
||||
(int, int) bar(int x, int y) {
|
||||
if (x < 0) {
|
||||
y = foo(y);
|
||||
x *= 2;
|
||||
if (x == -10) {
|
||||
return (111, y);
|
||||
}
|
||||
}
|
||||
return (x + 1, y);
|
||||
}
|
||||
(int, int) main(int x, int y) {
|
||||
(x, y) = bar(x, y);
|
||||
return (x, y * 10);
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 3 3 | 4 30
|
||||
TESTCASE | 0 | 3 -5 | 4 -50
|
||||
TESTCASE | 0 | 3 -4 | 4 -40
|
||||
TESTCASE | 0 | -5 3 | 111 40
|
||||
TESTCASE | 0 | -5 -5 | 111 1110
|
||||
TESTCASE | 0 | -5 -4 | 111 -70
|
||||
TESTCASE | 0 | -4 3 | -7 40
|
||||
TESTCASE | 0 | -4 -5 | -7 1110
|
||||
TESTCASE | 0 | -4 -4 | -7 -70
|
||||
-}
|
14
crypto/func/auto-tests/tests/w1.fc
Normal file
14
crypto/func/auto-tests/tests/w1.fc
Normal file
|
@ -0,0 +1,14 @@
|
|||
(int, int) main(int id) {
|
||||
if (id > 0) {
|
||||
if (id > 10) {
|
||||
return (2 * id, 3 * id);
|
||||
}
|
||||
}
|
||||
return (5, 6);
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 0 | 5 6
|
||||
TESTCASE | 0 | 4 | 5 6
|
||||
TESTCASE | 0 | 11 | 22 33
|
||||
-}
|
18
crypto/func/auto-tests/tests/w2.fc
Normal file
18
crypto/func/auto-tests/tests/w2.fc
Normal file
|
@ -0,0 +1,18 @@
|
|||
_ f(cs) {
|
||||
return (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8));
|
||||
}
|
||||
|
||||
_ main(cs) {
|
||||
var (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
|
||||
x11, x12, x13, x14, x15, x16, x17, x18, x19) = f(cs);
|
||||
return x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
|
||||
+ x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19;
|
||||
}
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | x{000102030405060708090a0b0c0d0e0f10111213} | 190
|
||||
-}
|
17
crypto/func/auto-tests/tests/w6.fc
Normal file
17
crypto/func/auto-tests/tests/w6.fc
Normal file
|
@ -0,0 +1,17 @@
|
|||
int main(int x) {
|
||||
int i = 0;
|
||||
;; int f = false;
|
||||
do {
|
||||
i = i + 1;
|
||||
if (i > 5) {
|
||||
return 1;
|
||||
}
|
||||
int f = (i * i == 64);
|
||||
} until (f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 0 | 1
|
||||
-}
|
24
crypto/func/auto-tests/tests/w7.fc
Normal file
24
crypto/func/auto-tests/tests/w7.fc
Normal file
|
@ -0,0 +1,24 @@
|
|||
int test(int y) method_id(1) {
|
||||
int x = 1;
|
||||
if (y > 0) {
|
||||
return 1;
|
||||
}
|
||||
return x > 0;
|
||||
}
|
||||
|
||||
int f(int y) method_id(2) {
|
||||
if (y > 0) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
_ main() { }
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 1 | 10 | 1
|
||||
TESTCASE | 1 | -5 | -1
|
||||
TESTCASE | 2 | 10 | 1
|
||||
TESTCASE | 2 | -5 | 2
|
||||
-}
|
14
crypto/func/auto-tests/tests/w9.fc
Normal file
14
crypto/func/auto-tests/tests/w9.fc
Normal file
|
@ -0,0 +1,14 @@
|
|||
_ main(s) {
|
||||
var (z, t) = (17, s);
|
||||
while (z > 0) {
|
||||
t = s;
|
||||
z -= 1;
|
||||
}
|
||||
return ~ t;
|
||||
}
|
||||
|
||||
{-
|
||||
method_id | in | out
|
||||
TESTCASE | 0 | 1 | -2
|
||||
TESTCASE | 0 | 5 | -6
|
||||
-}
|
|
@ -173,6 +173,74 @@ int emulate_mul(int a, int b) {
|
|||
return r;
|
||||
}
|
||||
|
||||
int emulate_and(int a, int b) {
|
||||
int both = a & b, any = a | b;
|
||||
int r = VarDescr::_Int;
|
||||
if (any & VarDescr::_Nan) {
|
||||
return r | VarDescr::_Nan;
|
||||
}
|
||||
r |= VarDescr::_Finite;
|
||||
if (any & VarDescr::_Zero) {
|
||||
return VarDescr::ConstZero;
|
||||
}
|
||||
r |= both & (VarDescr::_Even | VarDescr::_Odd);
|
||||
r |= both & (VarDescr::_Bit | VarDescr::_Bool);
|
||||
if (both & VarDescr::_Odd) {
|
||||
r |= VarDescr::_NonZero;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int emulate_or(int a, int b) {
|
||||
if (b & VarDescr::_Zero) {
|
||||
return a;
|
||||
} else if (a & VarDescr::_Zero) {
|
||||
return b;
|
||||
}
|
||||
int both = a & b, any = a | b;
|
||||
int r = VarDescr::_Int;
|
||||
if (any & VarDescr::_Nan) {
|
||||
return r | VarDescr::_Nan;
|
||||
}
|
||||
r |= VarDescr::_Finite;
|
||||
r |= any & VarDescr::_NonZero;
|
||||
r |= any & VarDescr::_Odd;
|
||||
r |= both & VarDescr::_Even;
|
||||
return r;
|
||||
}
|
||||
|
||||
int emulate_xor(int a, int b) {
|
||||
if (b & VarDescr::_Zero) {
|
||||
return a;
|
||||
} else if (a & VarDescr::_Zero) {
|
||||
return b;
|
||||
}
|
||||
int both = a & b, any = a | b;
|
||||
int r = VarDescr::_Int;
|
||||
if (any & VarDescr::_Nan) {
|
||||
return r | VarDescr::_Nan;
|
||||
}
|
||||
r |= VarDescr::_Finite;
|
||||
r |= both & VarDescr::_Even;
|
||||
if (both & VarDescr::_Odd) {
|
||||
r |= VarDescr::_Even;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int emulate_not(int a) {
|
||||
int f = VarDescr::_Even | VarDescr::_Odd;
|
||||
if ((a & f) && (~a & f)) {
|
||||
a ^= f;
|
||||
}
|
||||
f = VarDescr::_Pos | VarDescr::_Neg;
|
||||
if ((a & f) && (~a & f)) {
|
||||
a ^= f;
|
||||
}
|
||||
a &= ~(VarDescr::_Zero | VarDescr::_NonZero | VarDescr::_Bit);
|
||||
return a;
|
||||
}
|
||||
|
||||
int emulate_lshift(int a, int b) {
|
||||
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
|
||||
return VarDescr::_Int | VarDescr::_Nan;
|
||||
|
@ -427,6 +495,57 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
return exec_op("NEGATE", 1);
|
||||
}
|
||||
|
||||
AsmOp compile_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||
assert(res.size() == 1 && args.size() == 2);
|
||||
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||
if (x.is_int_const() && y.is_int_const()) {
|
||||
r.set_const(x.int_const & y.int_const);
|
||||
x.unused();
|
||||
y.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = emulate_and(x.val, y.val);
|
||||
return exec_op("AND", 2);
|
||||
}
|
||||
|
||||
AsmOp compile_or(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||
assert(res.size() == 1 && args.size() == 2);
|
||||
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||
if (x.is_int_const() && y.is_int_const()) {
|
||||
r.set_const(x.int_const | y.int_const);
|
||||
x.unused();
|
||||
y.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = emulate_or(x.val, y.val);
|
||||
return exec_op("OR", 2);
|
||||
}
|
||||
|
||||
AsmOp compile_xor(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||
assert(res.size() == 1 && args.size() == 2);
|
||||
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||
if (x.is_int_const() && y.is_int_const()) {
|
||||
r.set_const(x.int_const ^ y.int_const);
|
||||
x.unused();
|
||||
y.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = emulate_xor(x.val, y.val);
|
||||
return exec_op("XOR", 2);
|
||||
}
|
||||
|
||||
AsmOp compile_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||
assert(res.size() == 1 && args.size() == 1);
|
||||
VarDescr &r = res[0], &x = args[0];
|
||||
if (x.is_int_const()) {
|
||||
r.set_const(~x.int_const);
|
||||
x.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = emulate_not(x.val);
|
||||
return exec_op("NOT", 1);
|
||||
}
|
||||
|
||||
AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
||||
if (x.is_int_const() && y.is_int_const()) {
|
||||
r.set_const(x.int_const * y.int_const);
|
||||
|
@ -442,6 +561,7 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
|||
if (y.always_zero() && x.always_finite()) {
|
||||
// dubious optimization: NaN * 0 = ?
|
||||
r.set_const(y.int_const);
|
||||
x.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
if (*y.int_const == 1 && x.always_finite()) {
|
||||
|
@ -468,6 +588,7 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
|||
if (x.always_zero() && y.always_finite()) {
|
||||
// dubious optimization: NaN * 0 = ?
|
||||
r.set_const(x.int_const);
|
||||
y.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
if (*x.int_const == 1 && y.always_finite()) {
|
||||
|
@ -1000,10 +1121,10 @@ void define_builtins() {
|
|||
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
|
||||
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
|
||||
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
||||
define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2));
|
||||
define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2));
|
||||
define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2));
|
||||
define_builtin_func("~_", arith_un_op, AsmOp::Custom("NOT", 1));
|
||||
define_builtin_func("_&_", arith_bin_op, compile_and);
|
||||
define_builtin_func("_|_", arith_bin_op, compile_or);
|
||||
define_builtin_func("_^_", arith_bin_op, compile_xor);
|
||||
define_builtin_func("~_", arith_un_op, compile_not);
|
||||
define_builtin_func("^_+=_", arith_bin_op, compile_add);
|
||||
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
|
||||
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
|
||||
|
@ -1017,9 +1138,9 @@ void define_builtins() {
|
|||
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
|
||||
define_builtin_func("^_~>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
|
||||
define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
||||
define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2));
|
||||
define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2));
|
||||
define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2));
|
||||
define_builtin_func("^_&=_", arith_bin_op, compile_and);
|
||||
define_builtin_func("^_|=_", arith_bin_op, compile_or);
|
||||
define_builtin_func("^_^=_", arith_bin_op, compile_xor);
|
||||
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, -1));
|
||||
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 0));
|
||||
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 1));
|
||||
|
@ -1063,6 +1184,8 @@ void define_builtins() {
|
|||
AsmOp::Nop());
|
||||
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
|
||||
AsmOp::Custom("s0 DUMP", 1, 1), true);
|
||||
define_builtin_func("~strdump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
|
||||
AsmOp::Custom("STRDUMP", 1, 1), true);
|
||||
define_builtin_func("run_method0", TypeExpr::new_map(Int, Unit),
|
||||
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
|
||||
define_builtin_func("run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),
|
||||
|
|
127
crypto/func/func-main.cpp
Normal file
127
crypto/func/func-main.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. If you delete this exception statement
|
||||
from all source files in the program, then also delete it here.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "func.h"
|
||||
#include "parser/srcread.h"
|
||||
#include "parser/lexer.h"
|
||||
#include "parser/symtable.h"
|
||||
#include <getopt.h>
|
||||
#include <fstream>
|
||||
#include "git.h"
|
||||
|
||||
void usage(const char* progname) {
|
||||
std::cerr
|
||||
<< "usage: " << progname
|
||||
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
|
||||
"\tGenerates Fift TVM assembler code from a funC source\n"
|
||||
"-I\tEnables interactive mode (parse stdin)\n"
|
||||
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
|
||||
"-v\tIncreases verbosity level (extra information output into stderr)\n"
|
||||
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
|
||||
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
|
||||
"-O<level>\tSets optimization level (2 by default)\n"
|
||||
"-P\tEnvelope code into PROGRAM{ ... }END>c\n"
|
||||
"-S\tInclude stack layout comments in the output code\n"
|
||||
"-R\tInclude operation rewrite comments in the output code\n"
|
||||
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
|
||||
"-A and -P.\n"
|
||||
"\t-s\tOutput semantic version of FunC and exit\n"
|
||||
"\t-V<version>\tShow func build information\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
std::string output_filename;
|
||||
while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
|
||||
switch (i) {
|
||||
case 'A':
|
||||
funC::asm_preamble = true;
|
||||
break;
|
||||
case 'I':
|
||||
funC::interactive = true;
|
||||
break;
|
||||
case 'i':
|
||||
funC::indent = std::max(0, atoi(optarg));
|
||||
break;
|
||||
case 'o':
|
||||
output_filename = optarg;
|
||||
break;
|
||||
case 'O':
|
||||
funC::opt_level = std::max(0, atoi(optarg));
|
||||
break;
|
||||
case 'P':
|
||||
funC::program_envelope = true;
|
||||
break;
|
||||
case 'R':
|
||||
funC::op_rewrite_comments = true;
|
||||
break;
|
||||
case 'S':
|
||||
funC::stack_layout_comments = true;
|
||||
break;
|
||||
case 'v':
|
||||
++funC::verbosity;
|
||||
break;
|
||||
case 'W':
|
||||
funC::boc_output_filename = optarg;
|
||||
funC::asm_preamble = funC::program_envelope = true;
|
||||
break;
|
||||
case 's':
|
||||
std::cout << funC::func_version << "\n";
|
||||
std::exit(0);
|
||||
break;
|
||||
case 'V':
|
||||
std::cout << "FunC semantic version: v" << funC::func_version << "\n";
|
||||
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||
std::exit(0);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream *outs = &std::cout;
|
||||
|
||||
std::unique_ptr<std::fstream> fs;
|
||||
if (!output_filename.empty()) {
|
||||
fs = std::make_unique<std::fstream>(output_filename, fs->trunc | fs->out);
|
||||
if (!fs->is_open()) {
|
||||
std::cerr << "failed to create output file " << output_filename << '\n';
|
||||
return 2;
|
||||
}
|
||||
outs = fs.get();
|
||||
}
|
||||
|
||||
std::vector<std::string> sources;
|
||||
|
||||
while (optind < argc) {
|
||||
sources.push_back(std::string(argv[optind++]));
|
||||
}
|
||||
|
||||
return funC::func_proceed(sources, *outs, std::cerr);
|
||||
}
|
|
@ -28,16 +28,14 @@
|
|||
#include "func.h"
|
||||
#include "parser/srcread.h"
|
||||
#include "parser/lexer.h"
|
||||
#include "parser/symtable.h"
|
||||
#include <getopt.h>
|
||||
#include <fstream>
|
||||
#include "git.h"
|
||||
|
||||
namespace funC {
|
||||
|
||||
int verbosity, indent, opt_level = 2;
|
||||
bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
|
||||
std::ostream* outs = &std::cout;
|
||||
bool interactive = false;
|
||||
std::string generated_from, boc_output_filename;
|
||||
|
||||
/*
|
||||
|
@ -46,58 +44,59 @@ std::string generated_from, boc_output_filename;
|
|||
*
|
||||
*/
|
||||
|
||||
void generate_output_func(SymDef* func_sym) {
|
||||
void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &errs) {
|
||||
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||
assert(func_val);
|
||||
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
||||
if (verbosity >= 2) {
|
||||
std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
|
||||
errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
|
||||
}
|
||||
if (!func_val->code) {
|
||||
std::cerr << "( function `" << name << "` undefined )\n";
|
||||
errs << "( function `" << name << "` undefined )\n";
|
||||
throw src::ParseError(func_sym->loc, name);
|
||||
} else {
|
||||
CodeBlob& code = *(func_val->code);
|
||||
if (verbosity >= 3) {
|
||||
code.print(std::cerr, 9);
|
||||
code.print(errs, 9);
|
||||
}
|
||||
code.simplify_var_types();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after simplify_var_types: \n";
|
||||
code.print(std::cerr, 0);
|
||||
errs << "after simplify_var_types: \n";
|
||||
code.print(errs, 0);
|
||||
}
|
||||
code.prune_unreachable_code();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after prune_unreachable: \n";
|
||||
code.print(std::cerr, 0);
|
||||
errs << "after prune_unreachable: \n";
|
||||
code.print(errs, 0);
|
||||
}
|
||||
code.split_vars(true);
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after split_vars: \n";
|
||||
code.print(std::cerr, 0);
|
||||
errs << "after split_vars: \n";
|
||||
code.print(errs, 0);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
code.compute_used_code_vars();
|
||||
if (verbosity >= 4) {
|
||||
std::cerr << "after compute_used_vars: \n";
|
||||
code.print(std::cerr, 6);
|
||||
errs << "after compute_used_vars: \n";
|
||||
code.print(errs, 6);
|
||||
}
|
||||
code.fwd_analyze();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after fwd_analyze: \n";
|
||||
code.print(std::cerr, 6);
|
||||
errs << "after fwd_analyze: \n";
|
||||
code.print(errs, 6);
|
||||
}
|
||||
code.prune_unreachable_code();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after prune_unreachable: \n";
|
||||
code.print(std::cerr, 6);
|
||||
errs << "after prune_unreachable: \n";
|
||||
code.print(errs, 6);
|
||||
}
|
||||
}
|
||||
code.mark_noreturn();
|
||||
if (verbosity >= 3) {
|
||||
code.print(std::cerr, 15);
|
||||
code.print(errs, 15);
|
||||
}
|
||||
if (verbosity >= 2) {
|
||||
std::cerr << "\n---------- resulting code for " << name << " -------------\n";
|
||||
errs << "\n---------- resulting code for " << name << " -------------\n";
|
||||
}
|
||||
bool inline_func = (func_val->flags & 1);
|
||||
bool inline_ref = (func_val->flags & 2);
|
||||
|
@ -107,7 +106,7 @@ void generate_output_func(SymDef* func_sym) {
|
|||
} else if (inline_ref) {
|
||||
modifier = "REF";
|
||||
}
|
||||
*outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
|
||||
outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
|
||||
int mode = 0;
|
||||
if (stack_layout_comments) {
|
||||
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
|
||||
|
@ -120,145 +119,71 @@ void generate_output_func(SymDef* func_sym) {
|
|||
if (fv && (fv->flags & 1) && code.ops->noreturn()) {
|
||||
mode |= Stack::_InlineFunc;
|
||||
}
|
||||
code.generate_code(*outs, mode, indent + 1);
|
||||
*outs << std::string(indent * 2, ' ') << "}>\n";
|
||||
code.generate_code(outs, mode, indent + 1);
|
||||
outs << std::string(indent * 2, ' ') << "}>\n";
|
||||
if (verbosity >= 2) {
|
||||
std::cerr << "--------------\n";
|
||||
errs << "--------------\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int generate_output() {
|
||||
int generate_output(std::ostream &outs, std::ostream &errs) {
|
||||
if (asm_preamble) {
|
||||
*outs << "\"Asm.fif\" include\n";
|
||||
outs << "\"Asm.fif\" include\n";
|
||||
}
|
||||
*outs << "// automatically generated from " << generated_from << std::endl;
|
||||
outs << "// automatically generated from " << generated_from << std::endl;
|
||||
if (program_envelope) {
|
||||
*outs << "PROGRAM{\n";
|
||||
outs << "PROGRAM{\n";
|
||||
}
|
||||
for (SymDef* func_sym : glob_func) {
|
||||
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||
assert(func_val);
|
||||
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
||||
*outs << std::string(indent * 2, ' ');
|
||||
outs << std::string(indent * 2, ' ');
|
||||
if (func_val->method_id.is_null()) {
|
||||
*outs << "DECLPROC " << name << "\n";
|
||||
outs << "DECLPROC " << name << "\n";
|
||||
} else {
|
||||
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||
outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||
}
|
||||
}
|
||||
for (SymDef* gvar_sym : glob_vars) {
|
||||
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
|
||||
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
|
||||
*outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
|
||||
outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
|
||||
}
|
||||
int errors = 0;
|
||||
for (SymDef* func_sym : glob_func) {
|
||||
try {
|
||||
generate_output_func(func_sym);
|
||||
generate_output_func(func_sym, outs, errs);
|
||||
} catch (src::Error& err) {
|
||||
std::cerr << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n"
|
||||
errs << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n"
|
||||
<< err << std::endl;
|
||||
++errors;
|
||||
}
|
||||
}
|
||||
if (program_envelope) {
|
||||
*outs << "}END>c\n";
|
||||
outs << "}END>c\n";
|
||||
}
|
||||
if (!boc_output_filename.empty()) {
|
||||
*outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n";
|
||||
outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n";
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
} // namespace funC
|
||||
|
||||
void usage(const char* progname) {
|
||||
std::cerr
|
||||
<< "usage: " << progname
|
||||
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
|
||||
"\tGenerates Fift TVM assembler code from a funC source\n"
|
||||
"-I\tEnables interactive mode (parse stdin)\n"
|
||||
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
|
||||
"-v\tIncreases verbosity level (extra information output into stderr)\n"
|
||||
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
|
||||
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
|
||||
"-O<level>\tSets optimization level (2 by default)\n"
|
||||
"-P\tEnvelope code into PROGRAM{ ... }END>c\n"
|
||||
"-S\tInclude stack layout comments in the output code\n"
|
||||
"-R\tInclude operation rewrite comments in the output code\n"
|
||||
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
|
||||
"-A and -P.\n"
|
||||
"\t-s\tOutput semantic version of FunC and exit\n"
|
||||
"\t-V<version>\tShow func build information\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
void output_inclusion_stack() {
|
||||
void output_inclusion_stack(std::ostream &errs) {
|
||||
while (!funC::inclusion_locations.empty()) {
|
||||
src::SrcLocation loc = funC::inclusion_locations.top();
|
||||
funC::inclusion_locations.pop();
|
||||
if (loc.fdescr) {
|
||||
std::cerr << "note: included from ";
|
||||
loc.show(std::cerr);
|
||||
std::cerr << std::endl;
|
||||
errs << "note: included from ";
|
||||
loc.show(errs);
|
||||
errs << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string output_filename;
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
bool interactive = false;
|
||||
while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
|
||||
switch (i) {
|
||||
case 'A':
|
||||
funC::asm_preamble = true;
|
||||
break;
|
||||
case 'I':
|
||||
interactive = true;
|
||||
break;
|
||||
case 'i':
|
||||
funC::indent = std::max(0, atoi(optarg));
|
||||
break;
|
||||
case 'o':
|
||||
output_filename = optarg;
|
||||
break;
|
||||
case 'O':
|
||||
funC::opt_level = std::max(0, atoi(optarg));
|
||||
break;
|
||||
case 'P':
|
||||
funC::program_envelope = true;
|
||||
break;
|
||||
case 'R':
|
||||
funC::op_rewrite_comments = true;
|
||||
break;
|
||||
case 'S':
|
||||
funC::stack_layout_comments = true;
|
||||
break;
|
||||
case 'v':
|
||||
++funC::verbosity;
|
||||
break;
|
||||
case 'W':
|
||||
funC::boc_output_filename = optarg;
|
||||
funC::asm_preamble = funC::program_envelope = true;
|
||||
break;
|
||||
case 's':
|
||||
std::cout << funC::func_version << "\n";
|
||||
std::exit(0);
|
||||
break;
|
||||
case 'V':
|
||||
std::cout << "FunC semantic version: v" << funC::func_version << "\n";
|
||||
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||
std::exit(0);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, std::ostream &errs) {
|
||||
if (funC::program_envelope && !funC::indent) {
|
||||
funC::indent = 1;
|
||||
}
|
||||
|
@ -268,12 +193,11 @@ int main(int argc, char* const argv[]) {
|
|||
|
||||
int ok = 0, proc = 0;
|
||||
try {
|
||||
while (optind < argc) {
|
||||
// funC::generated_from += std::string{"`"} + argv[optind] + "` ";
|
||||
ok += funC::parse_source_file(argv[optind++]);
|
||||
for (auto src : sources) {
|
||||
ok += funC::parse_source_file(src.c_str());
|
||||
proc++;
|
||||
}
|
||||
if (interactive) {
|
||||
if (funC::interactive) {
|
||||
funC::generated_from += "stdin ";
|
||||
ok += funC::parse_source_stdin();
|
||||
proc++;
|
||||
|
@ -284,29 +208,24 @@ int main(int argc, char* const argv[]) {
|
|||
if (!proc) {
|
||||
throw src::Fatal{"no source files, no output"};
|
||||
}
|
||||
std::unique_ptr<std::fstream> fs;
|
||||
if (!output_filename.empty()) {
|
||||
fs = std::make_unique<std::fstream>(output_filename, fs->trunc | fs->out);
|
||||
if (!fs->is_open()) {
|
||||
std::cerr << "failed to create output file " << output_filename << '\n';
|
||||
return 2;
|
||||
}
|
||||
funC::outs = fs.get();
|
||||
}
|
||||
funC::generate_output();
|
||||
return funC::generate_output(outs, errs);
|
||||
} catch (src::Fatal& fatal) {
|
||||
std::cerr << "fatal: " << fatal << std::endl;
|
||||
output_inclusion_stack();
|
||||
std::exit(1);
|
||||
errs << "fatal: " << fatal << std::endl;
|
||||
output_inclusion_stack(errs);
|
||||
return 2;
|
||||
} catch (src::Error& error) {
|
||||
std::cerr << error << std::endl;
|
||||
output_inclusion_stack();
|
||||
std::exit(1);
|
||||
errs << error << std::endl;
|
||||
output_inclusion_stack(errs);
|
||||
return 2;
|
||||
} catch (funC::UnifyError& unif_err) {
|
||||
std::cerr << "fatal: ";
|
||||
unif_err.print_message(std::cerr);
|
||||
std::cerr << std::endl;
|
||||
output_inclusion_stack();
|
||||
std::exit(1);
|
||||
errs << "fatal: ";
|
||||
unif_err.print_message(errs);
|
||||
errs << std::endl;
|
||||
output_inclusion_stack(errs);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace funC
|
|
@ -39,7 +39,7 @@ extern std::string generated_from;
|
|||
|
||||
constexpr int optimize_depth = 20;
|
||||
|
||||
const std::string func_version{"0.2.0"};
|
||||
const std::string func_version{"0.3.0"};
|
||||
|
||||
enum Keyword {
|
||||
_Eof = -1,
|
||||
|
@ -1631,6 +1631,7 @@ inline compile_func_t make_ext_compile(AsmOp op) {
|
|||
struct SymValAsmFunc : SymValFunc {
|
||||
simple_compile_func_t simple_compile;
|
||||
compile_func_t ext_compile;
|
||||
td::uint64 crc;
|
||||
~SymValAsmFunc() override = default;
|
||||
SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false)
|
||||
: SymValFunc(-1, ft, impure), simple_compile(make_simple_compile(_macro)) {
|
||||
|
@ -1665,4 +1666,19 @@ AsmOp push_const(td::RefInt256 x);
|
|||
|
||||
void define_builtins();
|
||||
|
||||
|
||||
extern int verbosity, indent, opt_level;
|
||||
extern bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble, interactive;
|
||||
extern std::string generated_from, boc_output_filename;
|
||||
|
||||
/*
|
||||
*
|
||||
* OUTPUT CODE GENERATOR
|
||||
*
|
||||
*/
|
||||
|
||||
int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, std::ostream &errs);
|
||||
|
||||
} // namespace funC
|
||||
|
||||
|
||||
|
|
|
@ -254,9 +254,7 @@ void parse_const_decl(Lexer& lex) {
|
|||
if (!sym_def) {
|
||||
lex.cur().error_at("cannot define global symbol `", "`");
|
||||
}
|
||||
if (sym_def->value) {
|
||||
lex.cur().error_at("global symbol `", "` already exists");
|
||||
}
|
||||
Lexem ident = lex.cur();
|
||||
lex.next();
|
||||
if (lex.tp() != '=') {
|
||||
lex.cur().error_at("expected = instead of ", "");
|
||||
|
@ -273,10 +271,11 @@ void parse_const_decl(Lexer& lex) {
|
|||
if ((wanted_type != Expr::_None) && (x->cls != wanted_type)) {
|
||||
lex.cur().error("expression type does not match wanted type");
|
||||
}
|
||||
SymValConst* new_value = nullptr;
|
||||
if (x->cls == Expr::_Const) { // Integer constant
|
||||
sym_def->value = new SymValConst{const_cnt++, x->intval};
|
||||
new_value = new SymValConst{const_cnt++, x->intval};
|
||||
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
|
||||
sym_def->value = new SymValConst{const_cnt++, x->strval};
|
||||
new_value = new SymValConst{const_cnt++, x->strval};
|
||||
} else if (x->cls == Expr::_Apply) {
|
||||
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
|
||||
auto tmp_vars = x->pre_compile(code);
|
||||
|
@ -304,10 +303,20 @@ void parse_const_decl(Lexer& lex) {
|
|||
if (op.origin.is_null() || !op.origin->is_valid()) {
|
||||
lex.cur().error("precompiled expression did not result in a valid integer constant");
|
||||
}
|
||||
sym_def->value = new SymValConst{const_cnt++, op.origin};
|
||||
new_value = new SymValConst{const_cnt++, op.origin};
|
||||
} else {
|
||||
lex.cur().error("integer or slice literal or constant expected");
|
||||
}
|
||||
if (sym_def->value) {
|
||||
SymValConst* old_value = dynamic_cast<SymValConst*>(sym_def->value);
|
||||
Keyword new_type = new_value->get_type();
|
||||
if (!old_value || old_value->get_type() != new_type ||
|
||||
(new_type == _Int && *old_value->get_int_value() != *new_value->get_int_value()) ||
|
||||
(new_type == _Slice && old_value->get_str_value() != new_value->get_str_value())) {
|
||||
ident.error_at("global symbol `", "` already exists");
|
||||
}
|
||||
}
|
||||
sym_def->value = new_value;
|
||||
}
|
||||
|
||||
FormalArgList parse_formal_args(Lexer& lex) {
|
||||
|
@ -1261,19 +1270,48 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal
|
|||
lex.expect(')');
|
||||
}
|
||||
while (lex.tp() == _String) {
|
||||
asm_ops.push_back(AsmOp::Parse(lex.cur().str, cnt, width));
|
||||
lex.next();
|
||||
if (asm_ops.back().is_custom()) {
|
||||
cnt = width;
|
||||
std::string ops = lex.cur().str; // <op>\n<op>\n...
|
||||
std::string op;
|
||||
for (const char& c : ops) {
|
||||
if (c == '\n') {
|
||||
if (!op.empty()) {
|
||||
asm_ops.push_back(AsmOp::Parse(op, cnt, width));
|
||||
if (asm_ops.back().is_custom()) {
|
||||
cnt = width;
|
||||
}
|
||||
op.clear();
|
||||
}
|
||||
} else {
|
||||
op.push_back(c);
|
||||
}
|
||||
}
|
||||
if (!op.empty()) {
|
||||
asm_ops.push_back(AsmOp::Parse(op, cnt, width));
|
||||
if (asm_ops.back().is_custom()) {
|
||||
cnt = width;
|
||||
}
|
||||
}
|
||||
lex.next();
|
||||
}
|
||||
if (asm_ops.empty()) {
|
||||
throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"};
|
||||
}
|
||||
lex.expect(';');
|
||||
std::string crc_s;
|
||||
for (const AsmOp& asm_op : asm_ops) {
|
||||
crc_s += asm_op.op;
|
||||
}
|
||||
crc_s.push_back(impure);
|
||||
for (const int& x : arg_order) {
|
||||
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
|
||||
}
|
||||
for (const int& x : ret_order) {
|
||||
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
|
||||
}
|
||||
auto res = new SymValAsmFunc{func_type, asm_ops, impure};
|
||||
res->arg_order = std::move(arg_order);
|
||||
res->ret_order = std::move(ret_order);
|
||||
res->crc = td::crc64(crc_s);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1439,16 +1477,22 @@ void parse_func_def(Lexer& lex) {
|
|||
// code->print(std::cerr); // !!!DEBUG!!!
|
||||
func_sym_code->code = code;
|
||||
} else {
|
||||
Lexem asm_lexem = lex.cur();
|
||||
SymValAsmFunc* asm_func = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
|
||||
if (func_sym_val) {
|
||||
if (dynamic_cast<SymValCodeFunc*>(func_sym_val)) {
|
||||
lex.cur().error("function `"s + func_name.str + "` was already declared as an ordinary function");
|
||||
asm_lexem.error("function `"s + func_name.str + "` was already declared as an ordinary function");
|
||||
}
|
||||
if (dynamic_cast<SymValAsmFunc*>(func_sym_val)) {
|
||||
lex.cur().error("redefinition of built-in assembler function `"s + func_name.str + "`");
|
||||
SymValAsmFunc* asm_func_old = dynamic_cast<SymValAsmFunc*>(func_sym_val);
|
||||
if (asm_func_old) {
|
||||
if (asm_func->crc != asm_func_old->crc) {
|
||||
asm_lexem.error("redefinition of built-in assembler function `"s + func_name.str + "`");
|
||||
}
|
||||
} else {
|
||||
asm_lexem.error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
|
||||
}
|
||||
lex.cur().error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
|
||||
}
|
||||
func_sym->value = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
|
||||
func_sym->value = asm_func;
|
||||
}
|
||||
if (method_id.not_null()) {
|
||||
auto val = dynamic_cast<SymVal*>(func_sym->value);
|
||||
|
@ -1657,7 +1701,14 @@ bool parse_source_file(const char* filename, src::Lexem lex) {
|
|||
throw src::Fatal{msg};
|
||||
}
|
||||
}
|
||||
std::string real_filename = td::realpath(td::CSlice(filename)).move_as_ok();
|
||||
|
||||
auto path_res = td::realpath(td::CSlice(filename));
|
||||
if (path_res.is_error()) {
|
||||
auto error = path_res.move_as_error();
|
||||
lex.error(error.message().c_str());
|
||||
return false;
|
||||
}
|
||||
std::string real_filename = path_res.move_as_ok();
|
||||
if (std::count(source_files.begin(), source_files.end(), real_filename)) {
|
||||
if (verbosity >= 2) {
|
||||
if (lex.tp) {
|
||||
|
|
15
crypto/func/test/co2.fc
Normal file
15
crypto/func/test/co2.fc
Normal file
|
@ -0,0 +1,15 @@
|
|||
const int x = 5;
|
||||
const slice s = "abacaba";
|
||||
const int y = 3;
|
||||
const slice s = "abacaba";
|
||||
const int x = 5;
|
||||
const int z = 4, z = 4;
|
||||
|
||||
int sdeq (slice s1, slice s2) asm "SDEQ";
|
||||
|
||||
() main() {
|
||||
throw_unless(101, x == 5);
|
||||
throw_unless(102, y == 3);
|
||||
throw_unless(103, z == 4);
|
||||
throw_unless(104, sdeq(s, "abacaba"));
|
||||
}
|
24
crypto/func/test/co3.fc
Normal file
24
crypto/func/test/co3.fc
Normal file
|
@ -0,0 +1,24 @@
|
|||
const val1 = 123456789;
|
||||
const val2 = 987654321;
|
||||
const val3 = 135792468;
|
||||
const val4 = 246813579;
|
||||
|
||||
const prec_and = val1 & val2;
|
||||
const prec_or = val1 | val2;
|
||||
const prec_xor = val1 ^ val2;
|
||||
const prec_logic = ((val1 & val2) | val3) ^ val4;
|
||||
const prec_nand = val1 & (~ val2);
|
||||
|
||||
int get_and() { return prec_and; }
|
||||
int get_or() { return prec_or; }
|
||||
int get_xor() { return prec_xor; }
|
||||
int get_logic() { return prec_logic; }
|
||||
int get_nand() { return prec_nand; }
|
||||
|
||||
_ main() {
|
||||
throw_unless(101, get_and() == 39471121);
|
||||
throw_unless(102, get_or() == 1071639989);
|
||||
throw_unless(103, get_xor() == 1032168868);
|
||||
throw_unless(104, get_logic() == 82599134);
|
||||
throw_unless(105, get_nand() == 83985668);
|
||||
}
|
26
crypto/func/test/s2.fc
Normal file
26
crypto/func/test/s2.fc
Normal file
|
@ -0,0 +1,26 @@
|
|||
slice test1() asm """
|
||||
"Test" $>s
|
||||
PUSHSLICE
|
||||
""";
|
||||
|
||||
slice test2() asm """
|
||||
"Hello"
|
||||
" "
|
||||
"World"
|
||||
$+ $+ $>s
|
||||
PUSHSLICE
|
||||
""";
|
||||
|
||||
int sdeq (slice s1, slice s2) asm """SDEQ""";
|
||||
int sdeq (slice s1, slice s2) asm "SDEQ" "";
|
||||
int sdeq (slice s1, slice s2) asm "" """
|
||||
SDEQ
|
||||
""";
|
||||
|
||||
() main() {
|
||||
slice s = test1();
|
||||
throw_unless(101, sdeq(s, "Test"));
|
||||
|
||||
slice s = test2();
|
||||
throw_unless(102, sdeq(s, "Hello World"));
|
||||
}
|
131
crypto/funcfiftlib/funcfiftlib.cpp
Normal file
131
crypto/funcfiftlib/funcfiftlib.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. If you delete this exception statement
|
||||
from all source files in the program, then also delete it here.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "func/func.h"
|
||||
#include "git.h"
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
#include "fift/utils.h"
|
||||
#include "td/utils/base64.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
std::string escape_json(const std::string &s) {
|
||||
std::ostringstream o;
|
||||
for (auto c = s.cbegin(); c != s.cend(); c++) {
|
||||
switch (*c) {
|
||||
case '"': o << "\\\""; break;
|
||||
case '\\': o << "\\\\"; break;
|
||||
case '\b': o << "\\b"; break;
|
||||
case '\f': o << "\\f"; break;
|
||||
case '\n': o << "\\n"; break;
|
||||
case '\r': o << "\\r"; break;
|
||||
case '\t': o << "\\t"; break;
|
||||
default:
|
||||
if ('\x00' <= *c && *c <= '\x1f') {
|
||||
o << "\\u"
|
||||
<< std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(*c);
|
||||
} else {
|
||||
o << *c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return o.str();
|
||||
}
|
||||
|
||||
td::Result<std::string> compile_internal(char *config_json) {
|
||||
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
|
||||
auto &obj = input_json.get_object();
|
||||
|
||||
TRY_RESULT(opt_level, td::get_json_object_int_field(obj, "optLevel", false));
|
||||
TRY_RESULT(sources_obj, td::get_json_object_field(obj, "sources", td::JsonValue::Type::Array, false));
|
||||
|
||||
auto &sources_arr = sources_obj.get_array();
|
||||
|
||||
std::vector<std::string> sources;
|
||||
|
||||
for (auto &item : sources_arr) {
|
||||
sources.push_back(item.get_string().str());
|
||||
}
|
||||
|
||||
funC::opt_level = std::max(0, opt_level);
|
||||
funC::program_envelope = true;
|
||||
funC::verbosity = 0;
|
||||
funC::indent = 1;
|
||||
|
||||
std::ostringstream outs, errs;
|
||||
auto compile_res = funC::func_proceed(sources, outs, errs);
|
||||
|
||||
if (compile_res != 0) {
|
||||
return td::Status::Error(std::string("Func compilation error: ") + errs.str());
|
||||
}
|
||||
|
||||
TRY_RESULT(code_cell, fift::compile_asm(outs.str(), "/fiftlib/", false));
|
||||
TRY_RESULT(boc, vm::std_boc_serialize(code_cell));
|
||||
|
||||
td::JsonBuilder result_json;
|
||||
auto result_obj = result_json.enter_object();
|
||||
result_obj("status", "ok");
|
||||
result_obj("codeBoc", td::base64_encode(boc));
|
||||
result_obj("fiftCode", escape_json(outs.str()));
|
||||
result_obj.leave();
|
||||
|
||||
outs.clear();
|
||||
errs.clear();
|
||||
|
||||
return result_json.string_builder().as_cslice().str();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
const char* version() {
|
||||
auto version_json = td::JsonBuilder();
|
||||
auto obj = version_json.enter_object();
|
||||
obj("funcVersion", funC::func_version);
|
||||
obj("funcFiftLibCommitHash", GitMetadata::CommitSHA1());
|
||||
obj("funcFiftLibCommitDate", GitMetadata::CommitDate());
|
||||
obj.leave();
|
||||
return strdup(version_json.string_builder().as_cslice().c_str());
|
||||
}
|
||||
|
||||
const char *func_compile(char *config_json) {
|
||||
auto res = compile_internal(config_json);
|
||||
|
||||
if (res.is_error()) {
|
||||
auto result = res.move_as_error();
|
||||
auto error_res = td::JsonBuilder();
|
||||
auto error_o = error_res.enter_object();
|
||||
error_o("status", "error");
|
||||
error_o("message", result.message().str());
|
||||
error_o.leave();
|
||||
return strdup(error_res.string_builder().as_cslice().c_str());
|
||||
}
|
||||
|
||||
auto res_string = res.move_as_ok();
|
||||
|
||||
return strdup(res_string.c_str());
|
||||
}
|
||||
}
|
|
@ -125,8 +125,9 @@ int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) {
|
|||
}
|
||||
|
||||
Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts,
|
||||
std::string close_cmts, std::string quote_chars)
|
||||
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) {
|
||||
std::string close_cmts, std::string quote_chars, std::string multiline_quote)
|
||||
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined),
|
||||
multiline_quote(std::move(multiline_quote)) {
|
||||
std::memset(char_class, 0, sizeof(char_class));
|
||||
unsigned char activity = cc::active;
|
||||
for (char c : active_chars) {
|
||||
|
@ -171,6 +172,19 @@ void Lexer::set_spec(std::array<int, 3>& arr, std::string setup) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Lexer::is_multiline_quote(const char* begin, const char* end) {
|
||||
if (multiline_quote.empty()) {
|
||||
return false;
|
||||
}
|
||||
for (const char& c : multiline_quote) {
|
||||
if (begin == end || *begin != c) {
|
||||
return false;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lexer::expect(int exp_tp, const char* msg) {
|
||||
if (tp() != exp_tp) {
|
||||
throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " +
|
||||
|
@ -234,6 +248,37 @@ const Lexem& Lexer::next() {
|
|||
}
|
||||
return lexem.clear(src.here(), Lexem::Eof);
|
||||
}
|
||||
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
|
||||
src.advance(multiline_quote.size());
|
||||
const char* begin = src.get_ptr();
|
||||
const char* end = nullptr;
|
||||
SrcLocation here = src.here();
|
||||
std::string body;
|
||||
while (!src.is_eof()) {
|
||||
if (src.is_eoln()) {
|
||||
body.push_back('\n');
|
||||
src.load_line();
|
||||
continue;
|
||||
}
|
||||
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
|
||||
end = src.get_ptr();
|
||||
src.advance(multiline_quote.size());
|
||||
break;
|
||||
}
|
||||
body.push_back(src.cur_char());
|
||||
src.advance(1);
|
||||
}
|
||||
if (!end) {
|
||||
src.error("string extends past end of file");
|
||||
}
|
||||
lexem.set(body, here, Lexem::String);
|
||||
int c = src.cur_char();
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||
lexem.val = c;
|
||||
src.advance(1);
|
||||
}
|
||||
return lexem;
|
||||
}
|
||||
int c = src.cur_char();
|
||||
const char* end = src.get_ptr();
|
||||
if (is_quote_char(c) || c == '`') {
|
||||
|
|
|
@ -71,6 +71,7 @@ class Lexer {
|
|||
Lexem lexem, peek_lexem;
|
||||
unsigned char char_class[128];
|
||||
std::array<int, 3> eol_cmt, cmt_op, cmt_cl;
|
||||
std::string multiline_quote;
|
||||
enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 };
|
||||
|
||||
public:
|
||||
|
@ -78,7 +79,8 @@ class Lexer {
|
|||
return eof;
|
||||
}
|
||||
Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;",
|
||||
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"");
|
||||
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"",
|
||||
std::string multiline_quote = "\"\"\"");
|
||||
const Lexem& next();
|
||||
const Lexem& cur() const {
|
||||
return lexem;
|
||||
|
@ -109,6 +111,7 @@ class Lexer {
|
|||
|
||||
private:
|
||||
void set_spec(std::array<int, 3>& arr, std::string setup);
|
||||
bool is_multiline_quote(const char* begin, const char* end);
|
||||
};
|
||||
|
||||
} // namespace src
|
||||
|
|
|
@ -2423,7 +2423,7 @@ std::vector<const src::FileDescr*> source_fdescr;
|
|||
|
||||
bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
||||
src::SourceReader reader{is, fdescr};
|
||||
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/"};
|
||||
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/", ""};
|
||||
while (lex.tp() != src::_Eof) {
|
||||
parse_constructor_def(lex);
|
||||
// std::cerr << lex.cur().str << '\t' << lex.cur().name_str() << std::endl;
|
||||
|
|
|
@ -125,8 +125,11 @@ bool TupleT::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
|
|||
}
|
||||
|
||||
bool TLB::validate_ref_internal(int* ops, Ref<vm::Cell> cell_ref, bool weak) const {
|
||||
if (ops && --*ops < 0) {
|
||||
return false;
|
||||
if (ops) {
|
||||
if (*ops <= 0) {
|
||||
return false;
|
||||
}
|
||||
--*ops;
|
||||
}
|
||||
bool is_special;
|
||||
auto cs = load_cell_slice_special(std::move(cell_ref), is_special);
|
||||
|
|
|
@ -335,7 +335,7 @@ bool CellBuilder::store_ulong_rchk_bool(unsigned long long val, unsigned val_bit
|
|||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_long(long long val, unsigned val_bits) {
|
||||
return store_long_top(val << (64 - val_bits), val_bits);
|
||||
return store_long_top(val_bits == 0 ? 0 : (unsigned long long)val << (64 - val_bits), val_bits);
|
||||
}
|
||||
|
||||
CellBuilder& CellBuilder::store_long_top(unsigned long long val, unsigned top_bits) {
|
||||
|
|
|
@ -1055,44 +1055,47 @@ std::ostream& operator<<(std::ostream& os, Ref<CellSlice> cs_ref) {
|
|||
|
||||
// If can_be_special is not null, then it is allowed to load special cell
|
||||
// Flag whether loaded cell is actually special will be stored into can_be_special
|
||||
VirtualCell::LoadedCell load_cell_slice_impl(const Ref<Cell>& cell, bool* can_be_special) {
|
||||
auto* vm_state_interface = VmStateInterface::get();
|
||||
if (vm_state_interface) {
|
||||
vm_state_interface->register_cell_load(cell->get_hash());
|
||||
}
|
||||
auto r_loaded_cell = cell->load_cell();
|
||||
if (r_loaded_cell.is_error()) {
|
||||
throw VmError{Excno::cell_und, "failed to load cell"};
|
||||
}
|
||||
auto loaded_cell = r_loaded_cell.move_as_ok();
|
||||
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
|
||||
auto virtualization = loaded_cell.virt.get_virtualization();
|
||||
if (virtualization != 0) {
|
||||
throw VmVirtError{virtualization};
|
||||
VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_special) {
|
||||
while (true) {
|
||||
auto* vm_state_interface = VmStateInterface::get();
|
||||
if (vm_state_interface) {
|
||||
vm_state_interface->register_cell_load(cell->get_hash());
|
||||
}
|
||||
}
|
||||
if (can_be_special) {
|
||||
*can_be_special = loaded_cell.data_cell->is_special();
|
||||
} else if (loaded_cell.data_cell->is_special()) {
|
||||
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) {
|
||||
if (vm_state_interface) {
|
||||
CellSlice cs(std::move(loaded_cell));
|
||||
DCHECK(cs.size() == Cell::hash_bits + 8);
|
||||
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
|
||||
if (library_cell.not_null()) {
|
||||
//TODO: fix infinity loop
|
||||
return load_cell_slice_impl(library_cell, nullptr);
|
||||
}
|
||||
throw VmError{Excno::cell_und, "failed to load library cell"};
|
||||
auto r_loaded_cell = cell->load_cell();
|
||||
if (r_loaded_cell.is_error()) {
|
||||
throw VmError{Excno::cell_und, "failed to load cell"};
|
||||
}
|
||||
auto loaded_cell = r_loaded_cell.move_as_ok();
|
||||
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
|
||||
auto virtualization = loaded_cell.virt.get_virtualization();
|
||||
if (virtualization != 0) {
|
||||
throw VmVirtError{virtualization};
|
||||
}
|
||||
throw VmError{Excno::cell_und, "failed to load library cell (no vm_state_interface available)"};
|
||||
} else if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
|
||||
CHECK(loaded_cell.virt.get_virtualization() == 0);
|
||||
throw VmError{Excno::cell_und, "trying to load prunned cell"};
|
||||
}
|
||||
throw VmError{Excno::cell_und, "unexpected special cell"};
|
||||
if (can_be_special) {
|
||||
*can_be_special = loaded_cell.data_cell->is_special();
|
||||
} else if (loaded_cell.data_cell->is_special()) {
|
||||
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) {
|
||||
if (vm_state_interface) {
|
||||
CellSlice cs(std::move(loaded_cell));
|
||||
DCHECK(cs.size() == Cell::hash_bits + 8);
|
||||
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
|
||||
if (library_cell.not_null()) {
|
||||
cell = library_cell;
|
||||
can_be_special = nullptr;
|
||||
continue;
|
||||
}
|
||||
throw VmError{Excno::cell_und, "failed to load library cell"};
|
||||
}
|
||||
throw VmError{Excno::cell_und, "failed to load library cell (no vm_state_interface available)"};
|
||||
} else if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::PrunnedBranch) {
|
||||
CHECK(loaded_cell.virt.get_virtualization() == 0);
|
||||
throw VmError{Excno::cell_und, "trying to load prunned cell"};
|
||||
}
|
||||
throw VmError{Excno::cell_und, "unexpected special cell"};
|
||||
}
|
||||
return loaded_cell;
|
||||
}
|
||||
return loaded_cell;
|
||||
}
|
||||
|
||||
CellSlice load_cell_slice(const Ref<Cell>& cell) {
|
||||
|
|
|
@ -105,6 +105,43 @@ int exec_dump_value(VmState* st, unsigned arg) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int exec_dump_string(VmState* st) {
|
||||
VM_LOG(st) << "execute STRDUMP";
|
||||
if (!vm_debug_enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Stack& stack = st->get_stack();
|
||||
|
||||
if (stack.depth() > 0){
|
||||
auto cs = stack[0].as_slice();
|
||||
|
||||
if (cs.not_null()) { // wanted t_slice
|
||||
auto size = cs->size();
|
||||
|
||||
if (size % 8 == 0) {
|
||||
auto cnt = size / 8;
|
||||
|
||||
unsigned char tmp[128];
|
||||
cs.write().fetch_bytes(tmp, cnt);
|
||||
std::string s{tmp, tmp + cnt};
|
||||
|
||||
std::cerr << "#DEBUG#: " << s << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cerr << "#DEBUG#: slice contains not valid bits count" << std::endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
std::cerr << "#DEBUG#: is not a slice" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "#DEBUG#: s0 is absent" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_debug_ops(OpcodeTable& cp0) {
|
||||
using namespace std::placeholders;
|
||||
if (!vm_debug_enabled) {
|
||||
|
@ -113,7 +150,9 @@ void register_debug_ops(OpcodeTable& cp0) {
|
|||
} else {
|
||||
// NB: all non-redefined opcodes in fe00..feff should be redirected to dummy debug definitions
|
||||
cp0.insert(OpcodeInstr::mksimple(0xfe00, 16, "DUMPSTK", exec_dump_stack))
|
||||
.insert(OpcodeInstr::mkfixedrange(0xfe01, 0xfe20, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||
.insert(OpcodeInstr::mkfixedrange(0xfe01, 0xfe14, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||
.insert(OpcodeInstr::mksimple(0xfe14, 16,"STRDUMP", exec_dump_string))
|
||||
.insert(OpcodeInstr::mkfixedrange(0xfe15, 0xfe20, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||
.insert(OpcodeInstr::mkfixed(0xfe2, 12, 4, instr::dump_1sr("DUMP"), exec_dump_value))
|
||||
.insert(OpcodeInstr::mkfixedrange(0xfe30, 0xfef0, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||
.insert(OpcodeInstr::mkext(0xfef, 12, 4, dump_dummy_debug_str, exec_dummy_debug_str, compute_len_debug_str));
|
||||
|
|
|
@ -313,7 +313,7 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) {
|
|||
return td::Status::Error("bag of cells is too large");
|
||||
}
|
||||
|
||||
boc_writers::FileWriter writer{fd, info.total_size};
|
||||
boc_writers::FileWriter writer{fd, (size_t) info.total_size};
|
||||
auto store_ref = [&](unsigned long long value) {
|
||||
writer.store_uint(value, info.ref_byte_size);
|
||||
};
|
||||
|
|
|
@ -90,15 +90,27 @@ void StackEntry::dump(std::ostream& os) const {
|
|||
os << dec_string(as_int());
|
||||
break;
|
||||
case t_cell:
|
||||
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
|
||||
if (ref.not_null()) {
|
||||
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
|
||||
} else {
|
||||
os << "C{null}";
|
||||
}
|
||||
break;
|
||||
case t_builder:
|
||||
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
|
||||
if (ref.not_null()) {
|
||||
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
|
||||
} else {
|
||||
os << "BC{null}";
|
||||
}
|
||||
break;
|
||||
case t_slice: {
|
||||
os << "CS{";
|
||||
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
|
||||
os << '}';
|
||||
if (ref.not_null()) {
|
||||
os << "CS{";
|
||||
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
|
||||
os << '}';
|
||||
} else {
|
||||
os << "CS{null}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case t_string:
|
||||
|
|
|
@ -474,7 +474,7 @@ int exec_store_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) {
|
|||
stack.check_underflow(2);
|
||||
auto x = stack.pop_int();
|
||||
auto cbr = stack.pop_builder();
|
||||
unsigned len = ((x->bit_size(sgnd) + 7) >> 3);
|
||||
unsigned len = (((unsigned)x->bit_size(sgnd) + 7) >> 3);
|
||||
if (len >= (1u << len_bits)) {
|
||||
throw VmError{Excno::range_chk};
|
||||
}
|
||||
|
|
|
@ -503,7 +503,7 @@ int VmState::run() {
|
|||
|
||||
bool VmState::try_commit() {
|
||||
if (cr.d[0].not_null() && cr.d[1].not_null() && cr.d[0]->get_depth() <= max_data_depth &&
|
||||
cr.d[1]->get_depth() <= max_data_depth) {
|
||||
cr.d[1]->get_depth() <= max_data_depth && cr.d[0]->get_level() == 0 && cr.d[1]->get_level() == 0) {
|
||||
cstate.c4 = cr.d[0];
|
||||
cstate.c5 = cr.d[1];
|
||||
cstate.committed = true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue