diff --git a/src/libAtomVM/opcodes.h b/src/libAtomVM/opcodes.h index 25e17ed91c..1113d93137 100644 --- a/src/libAtomVM/opcodes.h +++ b/src/libAtomVM/opcodes.h @@ -47,6 +47,19 @@ #define OP_LOOP_REC_END 24 #define OP_WAIT 25 #define OP_WAIT_TIMEOUT 26 +// Unimplemented by BEAM from OTP 21 +// #define OP_M_PLUS 27 +// #define OP_M_MINUS 28 +// #define OP_M_TIMES 29 +// #define OP_M_DIV 30 +// #define OP_INT_DIV 31 +// #define OP_INT_REM 32 +// #define OP_INT_BAND 33 +// #define OP_INT_BOR 34 +// #define OP_INT_BXOR 35 +// #define OP_INT_BSL 36 +// #define OP_INT_BSR 37 +// #define OP_INT_BNOT 38 #define OP_IS_LT 39 #define OP_IS_GE 40 #define OP_IS_EQUAL 41 @@ -62,6 +75,8 @@ #define OP_IS_PORT 51 #define OP_IS_NIL 52 #define OP_IS_BINARY 53 +// Unimplemented by BEAM from OTP 21 +// #define OP_IS_CONSTANT 54 #define OP_IS_LIST 55 #define OP_IS_NONEMPTY_LIST 56 #define OP_IS_TUPLE 57 @@ -75,6 +90,8 @@ #define OP_GET_LIST 65 #define OP_GET_TUPLE_ELEMENT 66 #define OP_SET_TUPLE_ELEMENT 67 +// Unimplemented by BEAM from OTP 21 +// #define OP_PUT_STRING 68 #define OP_PUT_LIST 69 #define OP_PUT_TUPLE 70 #define OP_PUT 71 @@ -82,11 +99,26 @@ #define OP_IF_END 73 #define OP_CASE_END 74 #define OP_CALL_FUN 75 +// Unimplemented by BEAM from OTP 21 +// #define OP_MAKE_FUN 76 #define OP_IS_FUNCTION 77 #define OP_CALL_EXT_ONLY 78 +// Unimplemented by BEAM from OTP 21 +// #define OP_BS_START_MATCH 79 +// #define OP_BS_GET_INTEGER 80 +// #define OP_BS_GET_FLOAT 81 +// #define OP_BS_GET_BINARY 82 +// #define OP_BS_SKIP_BITS 83 +// #define OP_BS_TEST_FAIL 84 +// #define OP_BS_SAVE 85 +// #define OP_BS_RESTORE 86 +// #define OP_BS_INIT 87 +// #define OP_BS_FINAL 88 #define OP_BS_PUT_INTEGER 89 #define OP_BS_PUT_BINARY 90 #define OP_BS_PUT_STRING 92 +// Unimplemented by BEAM from OTP 21 +// #define OP_BS_NEED_BUF 93 #define OP_FCLEARERROR 94 #define OP_FCHECKERROR 95 #define OP_FMOVE 96 @@ -103,6 +135,8 @@ #define OP_TRY_CASE_END 107 #define OP_RAISE 108 #define OP_BS_INIT2 109 +// Unimplemented by BEAM from OTP 21 +// #define OP_BS_BITS_TO_BYTES 110 #define OP_BS_ADD 111 #define OP_APPLY 112 #define OP_APPLY_LAST 113 @@ -118,6 +152,10 @@ #define OP_BS_RESTORE2 123 #define OP_GC_BIF1 124 #define OP_GC_BIF2 125 +// Unimplemented by BEAM from OTP 21 +// #define OP_BS_FINAL2 126 +// #define OP_BS_BITS_TO_BYTES2 127 +// #define OP_PUT_LITERAL 128 #define OP_IS_BITSTR 129 #define OP_BS_CONTEXT_TO_BINARY 130 #define OP_BS_TEST_UNIT 131 @@ -152,23 +190,31 @@ #define OP_RAW_RAISE 161 #define OP_GET_HD 162 #define OP_GET_TL 163 +// Introduced in OTP 22 #define OP_PUT_TUPLE2 164 #define OP_BS_GET_TAIL 165 #define OP_BS_START_MATCH3 166 #define OP_BS_GET_POSITION 167 #define OP_BS_SET_POSITION 168 +// Introduced in OTP 23 #define OP_SWAP 169 #define OP_BS_START_MATCH4 170 +// Introduced in OTP 24 #define OP_MAKE_FUN3 171 #define OP_INIT_YREGS 172 #define OP_RECV_MARKER_BIND 173 #define OP_RECV_MARKER_CLEAR 174 #define OP_RECV_MARKER_RESERVE 175 #define OP_RECV_MARKER_USE 176 +// Introduced in OTP 25 #define OP_BS_CREATE_BIN 177 #define OP_CALL_FUN2 178 +#define OP_NIF_START 179 #define OP_BADRECORD 180 +// Introduced in OTP 26 #define OP_UPDATE_RECORD 181 #define OP_BS_MATCH 182 +// Introduced in OTP 27 +#define OP_EXECUTABLE_LINE 183 #endif diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index de7324c2df..a1014c56bc 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -45,9 +45,11 @@ #include "trace.h" // These constants can be used to reduce the size of the VM for a specific -// range of compiler versions +// range of compiler versions. It's difficult to assert whether a compiler still +// generates a given opcode, we're mostly using heuristics here. +// @bjorng suggested using compiler test suites and coverage to find out #define MINIMUM_OTP_COMPILER_VERSION 21 -#define MAXIMUM_OTP_COMPILER_VERSION 26 +#define MAXIMUM_OTP_COMPILER_VERSION 27 #ifdef __cplusplus extern "C" { @@ -2239,6 +2241,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } +#if MINIMUM_OTP_COMPILER_VERSION < 27 +// Not executable by OTP27 or higher case OP_ALLOCATE_ZERO: { uint32_t stack_need; DECODE_LITERAL(stack_need, pc); @@ -2264,8 +2268,10 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #endif break; } +#endif #if MINIMUM_OTP_COMPILER_VERSION <= 23 +// Not executable by OTP27 or higher case OP_ALLOCATE_HEAP_ZERO: { uint32_t stack_need; DECODE_LITERAL(stack_need, pc); @@ -2326,6 +2332,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } +#if MINIMUM_OTP_COMPILER_VERSION < 27 +// Not executable by OTP27 or higher (called init there) case OP_KILL: { uint32_t target; DECODE_YREG(target, pc); @@ -2337,6 +2345,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #endif break; } +#endif case OP_DEALLOCATE: { uint32_t n_words; @@ -3308,6 +3317,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) } #if MINIMUM_OTP_COMPILER_VERSION <= 21 +// Not executable by OTP27 or higher case OP_PUT_TUPLE: { uint32_t size; DECODE_LITERAL(size, pc); @@ -3546,6 +3556,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } +#if MINIMUM_OTP_COMPILER_VERSION < 27 +// Not executable by OTP27 or higher case OP_MAKE_FUN2: { uint32_t fun_index; DECODE_LITERAL(fun_index, pc) @@ -3561,6 +3573,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #endif break; } +#endif case OP_TRY: { DEST_REGISTER(dreg); @@ -4490,6 +4503,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #endif #if MINIMUM_OTP_COMPILER_VERSION <= 21 +// Not executable by OTP25 or higher case OP_BS_START_MATCH2: { uint32_t fail; DECODE_LABEL(fail, pc) @@ -4721,6 +4735,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #endif #if MINIMUM_OTP_COMPILER_VERSION <= 21 +// Not executable by OTP25 or higher case OP_BS_SAVE2: { term src; DECODE_COMPACT_TERM(src, pc); @@ -4750,6 +4765,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } +// Not executable by OTP25 or higher case OP_BS_RESTORE2: { term src; DECODE_COMPACT_TERM(src, pc); @@ -5074,6 +5090,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } +#if MINIMUM_OTP_COMPILER_VERSION < 25 +// Not executable by OTP25 or higher case OP_BS_CONTEXT_TO_BINARY: { // Do not check if dreg is a binary or not // In case it is not a binary or a match state, dreg will not be changed. @@ -5111,6 +5129,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #endif break; } +#endif case OP_APPLY: { #ifdef IMPL_EXECUTE_LOOP @@ -5492,6 +5511,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #if MINIMUM_OTP_COMPILER_VERSION <= 23 //TODO: stub, implement recv_mark/1 //it looks like it can be safely left unimplemented +// Not executable by OTP27 or higher case OP_RECV_MARK: { uint32_t label; DECODE_LABEL(label, pc); @@ -5503,6 +5523,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) //TODO: stub, implement recv_set/1 //it looks like it can be safely left unimplemented +// Not executable by OTP27 or higher case OP_RECV_SET: { uint32_t label; DECODE_LABEL(label, pc); @@ -5853,12 +5874,14 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) } #if MINIMUM_OTP_COMPILER_VERSION <= 23 +// Not executable by OTP27 or higher case OP_FCLEARERROR: { // This can be a noop as we raise from bifs TRACE("fclearerror/0\n"); break; } +// Not executable by OTP27 or higher case OP_FCHECKERROR: { // This can be a noop as we raise from bifs int fail_label; @@ -6678,6 +6701,11 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } + case OP_NIF_START: { + TRACE("nif_start/0\n"); + break; + } + case OP_BADRECORD: { TRACE("badrecord/1\n"); @@ -7008,6 +7036,22 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) } #endif +#if MAXIMUM_OTP_COMPILER_VERSION >= 27 + case OP_EXECUTABLE_LINE: { + uint32_t arg1; + DECODE_COMPACT_TERM(arg1, pc); + + uint32_t arg2; + DECODE_COMPACT_TERM(arg2, pc); + + TRACE("executable_line/2 arg1=0x%lx, arg2=0x%lx\n", arg1, arg2); + + USED_BY_TRACE(arg1); + USED_BY_TRACE(arg2); + break; + } +#endif + default: printf("Undecoded opcode: %i\n", pc[-1]); #ifdef IMPL_EXECUTE_LOOP diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt index 1026a2397c..04632724ca 100644 --- a/tests/erlang_tests/CMakeLists.txt +++ b/tests/erlang_tests/CMakeLists.txt @@ -21,13 +21,17 @@ cmake_minimum_required (VERSION 3.13) project (erlang_tests) -function(compile_erlang module_name) - add_custom_command( - OUTPUT ${module_name}.beam - COMMAND erlc ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl - COMMENT "Compiling ${module_name}.erl" - ) +function(compile_erlang) + cmake_parse_arguments(arg "" "" "ERLC_OPTIONS MODULES" ${ARGN}) + + foreach(module_name ${arg_UNPARSED_ARGUMENTS} ${arg_MODULES}) + add_custom_command( + OUTPUT ${module_name}.beam + COMMAND erlc ${arg_ERLC_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl + COMMENT "Compiling ${module_name}.erl" + ) + endforeach() endfunction() set(TO_HRL_PATH ${CMAKE_CURRENT_LIST_DIR}) @@ -494,6 +498,9 @@ compile_erlang(test_close_avm_pack) compile_erlang(test_module_info) +# -compile attribute doesn't seem to work in OTP27 +compile_erlang(ERLC_OPTIONS +line_coverage MODULES test_executable_line) + compile_erlang(int64_build_binary) compile_erlang(test_crypto_strong_rand_bytes) @@ -970,6 +977,7 @@ add_custom_target(erlang_test_modules DEPENDS test_close_avm_pack.beam test_module_info.beam + test_executable_line.beam int64_build_binary.beam diff --git a/tests/erlang_tests/test_executable_line.erl b/tests/erlang_tests/test_executable_line.erl new file mode 100644 index 0000000000..699676d69e --- /dev/null +++ b/tests/erlang_tests/test_executable_line.erl @@ -0,0 +1,41 @@ +% +% This file is part of AtomVM. +% +% Copyright 2024 Paul Guyot +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. +% +% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +% + +-module(test_executable_line). +-compile([line_coverage]). +-export([start/0]). + + +start() -> + ok = test_executable_line(), + 0. + +test_executable_line() -> + ModuleInfo = ?MODULE:module_info(), + CompileList = proplists_get_value(compile, ModuleInfo), + true = is_list(CompileList), + OptionsList = proplists_get_value(options, CompileList), + erlang:display({compile_options, OptionsList}), + erlang:display({emu_flavor, erlang:system_info(emu_flavor)}), + ok. + +proplists_get_value(_Key, []) -> undefined; +proplists_get_value(Key, [{Key, Value} | _Tail]) -> Value; +proplists_get_value(Key, [_Pair | Tail]) -> proplists_get_value(Key, Tail). diff --git a/tests/test.c b/tests/test.c index 859c66b61c..9b14378d0d 100644 --- a/tests/test.c +++ b/tests/test.c @@ -532,6 +532,7 @@ struct Test tests[] = { TEST_CASE_ATOMVM_ONLY(test_close_avm_pack, 0), TEST_CASE(test_module_info), + TEST_CASE(test_executable_line), // noisy tests, keep them at the end TEST_CASE_EXPECTED(spawn_opt_monitor_normal, 1),