Skip to content

Commit 60d9f4d

Browse files
committed
Allow for less opcodes based on version of OTP compiler
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 <pguyot@kallisys.net>
1 parent 24084c5 commit 60d9f4d

File tree

6 files changed

+164
-13
lines changed

6 files changed

+164
-13
lines changed

.github/workflows/run-tests-with-beam.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,21 @@ jobs:
7171
container: erlang:27
7272

7373
# This is ARM64
74-
- os: "macos-14"
74+
- os: "macos-15"
7575
otp: "24"
7676
path_prefix: "/opt/homebrew/opt/erlang@24/bin:"
7777

78-
- os: "macos-14"
78+
- os: "macos-15"
7979
otp: "25"
8080
path_prefix: "/opt/homebrew/opt/erlang@25/bin:"
8181

82-
- os: "macos-14"
82+
- os: "macos-15"
8383
otp: "26"
8484
path_prefix: "/opt/homebrew/opt/erlang@26/bin:"
85+
86+
- os: "macos-15"
87+
otp: "27"
88+
path_prefix: "/opt/homebrew/opt/erlang@27/bin:"
8589
steps:
8690
# Setup
8791
- name: "Checkout repo"
@@ -107,7 +111,7 @@ jobs:
107111
id: cache
108112
with:
109113
path: 'build/tests/**/*.beam'
110-
key: ${{ matrix.otp }}-${{ hashFiles('**/run-tests-with-beam.yaml', 'tests/**/*.erl') }}
114+
key: ${{ matrix.otp }}-${{ hashFiles('**/run-tests-with-beam.yaml', 'tests/**/*.erl', 'tests/**/CMakeLists.txt') }}
111115

112116
- name: "Build: run cmake"
113117
working-directory: build

src/libAtomVM/opcodes.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@
4747
#define OP_LOOP_REC_END 24
4848
#define OP_WAIT 25
4949
#define OP_WAIT_TIMEOUT 26
50+
// Unimplemented by BEAM from OTP 21
51+
// #define OP_M_PLUS 27
52+
// #define OP_M_MINUS 28
53+
// #define OP_M_TIMES 29
54+
// #define OP_M_DIV 30
55+
// #define OP_INT_DIV 31
56+
// #define OP_INT_REM 32
57+
// #define OP_INT_BAND 33
58+
// #define OP_INT_BOR 34
59+
// #define OP_INT_BXOR 35
60+
// #define OP_INT_BSL 36
61+
// #define OP_INT_BSR 37
62+
// #define OP_INT_BNOT 38
5063
#define OP_IS_LT 39
5164
#define OP_IS_GE 40
5265
#define OP_IS_EQUAL 41
@@ -62,6 +75,8 @@
6275
#define OP_IS_PORT 51
6376
#define OP_IS_NIL 52
6477
#define OP_IS_BINARY 53
78+
// Unimplemented by BEAM from OTP 21
79+
// #define OP_IS_CONSTANT 54
6580
#define OP_IS_LIST 55
6681
#define OP_IS_NONEMPTY_LIST 56
6782
#define OP_IS_TUPLE 57
@@ -75,18 +90,35 @@
7590
#define OP_GET_LIST 65
7691
#define OP_GET_TUPLE_ELEMENT 66
7792
#define OP_SET_TUPLE_ELEMENT 67
93+
// Unimplemented by BEAM from OTP 21
94+
// #define OP_PUT_STRING 68
7895
#define OP_PUT_LIST 69
7996
#define OP_PUT_TUPLE 70
8097
#define OP_PUT 71
8198
#define OP_BADMATCH 72
8299
#define OP_IF_END 73
83100
#define OP_CASE_END 74
84101
#define OP_CALL_FUN 75
102+
// Unimplemented by BEAM from OTP 21
103+
// #define OP_MAKE_FUN 76
85104
#define OP_IS_FUNCTION 77
86105
#define OP_CALL_EXT_ONLY 78
106+
// Unimplemented by BEAM from OTP 21
107+
// #define OP_BS_START_MATCH 79
108+
// #define OP_BS_GET_INTEGER 80
109+
// #define OP_BS_GET_FLOAT 81
110+
// #define OP_BS_GET_BINARY 82
111+
// #define OP_BS_SKIP_BITS 83
112+
// #define OP_BS_TEST_FAIL 84
113+
// #define OP_BS_SAVE 85
114+
// #define OP_BS_RESTORE 86
115+
// #define OP_BS_INIT 87
116+
// #define OP_BS_FINAL 88
87117
#define OP_BS_PUT_INTEGER 89
88118
#define OP_BS_PUT_BINARY 90
89119
#define OP_BS_PUT_STRING 92
120+
// Unimplemented by BEAM from OTP 21
121+
// #define OP_BS_NEED_BUF 93
90122
#define OP_FCLEARERROR 94
91123
#define OP_FCHECKERROR 95
92124
#define OP_FMOVE 96
@@ -103,6 +135,8 @@
103135
#define OP_TRY_CASE_END 107
104136
#define OP_RAISE 108
105137
#define OP_BS_INIT2 109
138+
// Unimplemented by BEAM from OTP 21
139+
// #define OP_BS_BITS_TO_BYTES 110
106140
#define OP_BS_ADD 111
107141
#define OP_APPLY 112
108142
#define OP_APPLY_LAST 113
@@ -118,6 +152,10 @@
118152
#define OP_BS_RESTORE2 123
119153
#define OP_GC_BIF1 124
120154
#define OP_GC_BIF2 125
155+
// Unimplemented by BEAM from OTP 21
156+
// #define OP_BS_FINAL2 126
157+
// #define OP_BS_BITS_TO_BYTES2 127
158+
// #define OP_PUT_LITERAL 128
121159
#define OP_IS_BITSTR 129
122160
#define OP_BS_CONTEXT_TO_BINARY 130
123161
#define OP_BS_TEST_UNIT 131
@@ -152,23 +190,31 @@
152190
#define OP_RAW_RAISE 161
153191
#define OP_GET_HD 162
154192
#define OP_GET_TL 163
193+
// Introduced in OTP 22
155194
#define OP_PUT_TUPLE2 164
156195
#define OP_BS_GET_TAIL 165
157196
#define OP_BS_START_MATCH3 166
158197
#define OP_BS_GET_POSITION 167
159198
#define OP_BS_SET_POSITION 168
199+
// Introduced in OTP 23
160200
#define OP_SWAP 169
161201
#define OP_BS_START_MATCH4 170
202+
// Introduced in OTP 24
162203
#define OP_MAKE_FUN3 171
163204
#define OP_INIT_YREGS 172
164205
#define OP_RECV_MARKER_BIND 173
165206
#define OP_RECV_MARKER_CLEAR 174
166207
#define OP_RECV_MARKER_RESERVE 175
167208
#define OP_RECV_MARKER_USE 176
209+
// Introduced in OTP 25
168210
#define OP_BS_CREATE_BIN 177
169211
#define OP_CALL_FUN2 178
212+
#define OP_NIF_START 179
170213
#define OP_BADRECORD 180
214+
// Introduced in OTP 26
171215
#define OP_UPDATE_RECORD 181
172216
#define OP_BS_MATCH 182
217+
// Introduced in OTP 27
218+
#define OP_EXECUTABLE_LINE 183
173219

174220
#endif

src/libAtomVM/opcodesswitch.h

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@
4545
#include "trace.h"
4646

4747
// These constants can be used to reduce the size of the VM for a specific
48-
// range of compiler versions
48+
// range of compiler versions. It's difficult to assert whether a compiler still
49+
// generates a given opcode, we're mostly using heuristics here.
50+
// @bjorng suggested using compiler test suites and coverage to find out
4951
#define MINIMUM_OTP_COMPILER_VERSION 21
50-
#define MAXIMUM_OTP_COMPILER_VERSION 26
52+
#define MAXIMUM_OTP_COMPILER_VERSION 27
5153

5254
#ifdef __cplusplus
5355
extern "C" {
@@ -2239,6 +2241,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
22392241
break;
22402242
}
22412243

2244+
#if MINIMUM_OTP_COMPILER_VERSION < 27
2245+
// Not executable by OTP27 or higher
22422246
case OP_ALLOCATE_ZERO: {
22432247
uint32_t stack_need;
22442248
DECODE_LITERAL(stack_need, pc);
@@ -2264,8 +2268,10 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
22642268
#endif
22652269
break;
22662270
}
2271+
#endif
22672272

22682273
#if MINIMUM_OTP_COMPILER_VERSION <= 23
2274+
// Not executable by OTP27 or higher
22692275
case OP_ALLOCATE_HEAP_ZERO: {
22702276
uint32_t stack_need;
22712277
DECODE_LITERAL(stack_need, pc);
@@ -2326,6 +2332,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
23262332
break;
23272333
}
23282334

2335+
#if MINIMUM_OTP_COMPILER_VERSION < 27
2336+
// Not executable by OTP27 or higher (called init there)
23292337
case OP_KILL: {
23302338
uint32_t target;
23312339
DECODE_YREG(target, pc);
@@ -2337,6 +2345,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
23372345
#endif
23382346
break;
23392347
}
2348+
#endif
23402349

23412350
case OP_DEALLOCATE: {
23422351
uint32_t n_words;
@@ -3308,6 +3317,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
33083317
}
33093318

33103319
#if MINIMUM_OTP_COMPILER_VERSION <= 21
3320+
// Not executable by OTP27 or higher
33113321
case OP_PUT_TUPLE: {
33123322
uint32_t size;
33133323
DECODE_LITERAL(size, pc);
@@ -3546,6 +3556,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
35463556
break;
35473557
}
35483558

3559+
#if MINIMUM_OTP_COMPILER_VERSION < 27
3560+
// Not executable by OTP27 or higher
35493561
case OP_MAKE_FUN2: {
35503562
uint32_t fun_index;
35513563
DECODE_LITERAL(fun_index, pc)
@@ -3561,6 +3573,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
35613573
#endif
35623574
break;
35633575
}
3576+
#endif
35643577

35653578
case OP_TRY: {
35663579
DEST_REGISTER(dreg);
@@ -4490,6 +4503,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
44904503
#endif
44914504

44924505
#if MINIMUM_OTP_COMPILER_VERSION <= 21
4506+
// Not executable by OTP25 or higher
44934507
case OP_BS_START_MATCH2: {
44944508
uint32_t fail;
44954509
DECODE_LABEL(fail, pc)
@@ -4721,6 +4735,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
47214735
#endif
47224736

47234737
#if MINIMUM_OTP_COMPILER_VERSION <= 21
4738+
// Not executable by OTP25 or higher
47244739
case OP_BS_SAVE2: {
47254740
term src;
47264741
DECODE_COMPACT_TERM(src, pc);
@@ -4750,6 +4765,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
47504765
break;
47514766
}
47524767

4768+
// Not executable by OTP25 or higher
47534769
case OP_BS_RESTORE2: {
47544770
term src;
47554771
DECODE_COMPACT_TERM(src, pc);
@@ -5074,6 +5090,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
50745090
break;
50755091
}
50765092

5093+
#if MINIMUM_OTP_COMPILER_VERSION < 25
5094+
// Not executable by OTP25 or higher
50775095
case OP_BS_CONTEXT_TO_BINARY: {
50785096
// Do not check if dreg is a binary or not
50795097
// 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)
51115129
#endif
51125130
break;
51135131
}
5132+
#endif
51145133

51155134
case OP_APPLY: {
51165135
#ifdef IMPL_EXECUTE_LOOP
@@ -5492,6 +5511,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
54925511
#if MINIMUM_OTP_COMPILER_VERSION <= 23
54935512
//TODO: stub, implement recv_mark/1
54945513
//it looks like it can be safely left unimplemented
5514+
// Not executable by OTP27 or higher
54955515
case OP_RECV_MARK: {
54965516
uint32_t label;
54975517
DECODE_LABEL(label, pc);
@@ -5503,6 +5523,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
55035523

55045524
//TODO: stub, implement recv_set/1
55055525
//it looks like it can be safely left unimplemented
5526+
// Not executable by OTP27 or higher
55065527
case OP_RECV_SET: {
55075528
uint32_t label;
55085529
DECODE_LABEL(label, pc);
@@ -5853,12 +5874,14 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
58535874
}
58545875

58555876
#if MINIMUM_OTP_COMPILER_VERSION <= 23
5877+
// Not executable by OTP27 or higher
58565878
case OP_FCLEARERROR: {
58575879
// This can be a noop as we raise from bifs
58585880
TRACE("fclearerror/0\n");
58595881
break;
58605882
}
58615883

5884+
// Not executable by OTP27 or higher
58625885
case OP_FCHECKERROR: {
58635886
// This can be a noop as we raise from bifs
58645887
int fail_label;
@@ -6678,6 +6701,11 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
66786701
break;
66796702
}
66806703

6704+
case OP_NIF_START: {
6705+
TRACE("nif_start/0\n");
6706+
break;
6707+
}
6708+
66816709
case OP_BADRECORD: {
66826710
TRACE("badrecord/1\n");
66836711

@@ -7008,6 +7036,21 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
70087036
}
70097037
#endif
70107038

7039+
#if MAXIMUM_OTP_COMPILER_VERSION >= 27
7040+
case OP_EXECUTABLE_LINE: {
7041+
term arg1;
7042+
DECODE_COMPACT_TERM(arg1, pc);
7043+
term arg2;
7044+
DECODE_COMPACT_TERM(arg2, pc);
7045+
7046+
TRACE("executable_line/2 arg1=0x%lx, arg2=0x%lx\n", arg1, arg2);
7047+
7048+
USED_BY_TRACE(arg1);
7049+
USED_BY_TRACE(arg2);
7050+
break;
7051+
}
7052+
#endif
7053+
70117054
default:
70127055
printf("Undecoded opcode: %i\n", pc[-1]);
70137056
#ifdef IMPL_EXECUTE_LOOP

tests/erlang_tests/CMakeLists.txt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@
2121
cmake_minimum_required (VERSION 3.13)
2222
project (erlang_tests)
2323

24-
function(compile_erlang module_name)
25-
add_custom_command(
26-
OUTPUT ${module_name}.beam
27-
COMMAND erlc ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl
28-
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl
29-
COMMENT "Compiling ${module_name}.erl"
30-
)
24+
function(compile_erlang)
25+
cmake_parse_arguments(arg "" "" "ERLC_OPTIONS;MODULES" ${ARGN})
26+
27+
foreach(module_name ${arg_UNPARSED_ARGUMENTS} ${arg_MODULES})
28+
add_custom_command(
29+
OUTPUT ${module_name}.beam
30+
COMMAND erlc ${arg_ERLC_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl
31+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl
32+
COMMENT "Compiling ${module_name}.erl"
33+
)
34+
endforeach()
3135
endfunction()
3236

3337
set(TO_HRL_PATH ${CMAKE_CURRENT_LIST_DIR})
@@ -494,6 +498,9 @@ compile_erlang(test_close_avm_pack)
494498

495499
compile_erlang(test_module_info)
496500

501+
# -compile attribute doesn't seem to work in OTP27
502+
compile_erlang(ERLC_OPTIONS +line_coverage MODULES test_executable_line)
503+
497504
compile_erlang(int64_build_binary)
498505

499506
compile_erlang(test_crypto_strong_rand_bytes)
@@ -970,6 +977,7 @@ add_custom_target(erlang_test_modules DEPENDS
970977
test_close_avm_pack.beam
971978

972979
test_module_info.beam
980+
test_executable_line.beam
973981

974982
int64_build_binary.beam
975983

0 commit comments

Comments
 (0)