Skip to content

Commit

Permalink
Allow for less opcodes based on version of OTP compiler
Browse files Browse the repository at this point in the history
Filter opcodes that are not supported by VM. Further filtering can be done
by looking at coverage of OTP compiler tests to figure out which opcodes
compilers no longer generate.

Also implement nif_start/0 (OTP25) and executable_line/2 (OTP27)

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Oct 20, 2024
1 parent 24084c5 commit 81ef28f
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 13 deletions.
12 changes: 8 additions & 4 deletions .github/workflows/run-tests-with-beam.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,21 @@ jobs:
container: erlang:27

# This is ARM64
- os: "macos-14"
- os: "macos-15"
otp: "24"
path_prefix: "/opt/homebrew/opt/erlang@24/bin:"

- os: "macos-14"
- os: "macos-15"
otp: "25"
path_prefix: "/opt/homebrew/opt/erlang@25/bin:"

- os: "macos-14"
- os: "macos-15"
otp: "26"
path_prefix: "/opt/homebrew/opt/erlang@26/bin:"

- os: "macos-15"
otp: "27"
path_prefix: "/opt/homebrew/opt/erlang@27/bin:"
steps:
# Setup
- name: "Checkout repo"
Expand All @@ -107,7 +111,7 @@ jobs:
id: cache
with:
path: 'build/tests/**/*.beam'
key: ${{ matrix.otp }}-${{ hashFiles('**/run-tests-with-beam.yaml', 'tests/**/*.erl') }}
key: ${{ matrix.otp }}-${{ hashFiles('**/run-tests-with-beam.yaml', 'tests/**/*.erl', 'tests/**/CMakeLists.txt') }}

- name: "Build: run cmake"
working-directory: build
Expand Down
46 changes: 46 additions & 0 deletions src/libAtomVM/opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -75,18 +90,35 @@
#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
#define OP_BADMATCH 72
#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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
47 changes: 45 additions & 2 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -2337,6 +2345,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
#endif
break;
}
#endif

case OP_DEALLOCATE: {
uint32_t n_words;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -3561,6 +3573,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
#endif
break;
}
#endif

case OP_TRY: {
DEST_REGISTER(dreg);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -5111,6 +5129,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
#endif
break;
}
#endif

case OP_APPLY: {
#ifdef IMPL_EXECUTE_LOOP
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -7008,6 +7036,21 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
}
#endif

#if MAXIMUM_OTP_COMPILER_VERSION >= 27
case OP_EXECUTABLE_LINE: {
term arg1;
DECODE_COMPACT_TERM(arg1, pc);
term 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
Expand Down
22 changes: 15 additions & 7 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
Loading

0 comments on commit 81ef28f

Please sign in to comment.