Skip to content

Commit db62ef6

Browse files
committed
Add support for FP opcodes
These opcodes are officially supported since R8. `fclearerror/0` and `fcheckerror/1` are noops as they are no longer implemented in OTP24 and the error is always checked by other OP_F* opcodes. No `badarith` exception is raised on underflow divisions, to be compatible with BEAM (which cannot check that as it uses `isfinite(3)`). The only checked exceptions are overflow and division by zero. The tests were updated to actually generate the FP opcodes with OTP21-24 compilers. A new test was added for div. Signed-off-by: Paul Guyot <[email protected]>
1 parent d9040e9 commit db62ef6

File tree

14 files changed

+625
-41
lines changed

14 files changed

+625
-41
lines changed

src/libAtomVM/context.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
#include "context.h"
2222

23+
#include <fenv.h>
24+
#include <math.h>
25+
2326
#include "dictionary.h"
2427
#include "globalcontext.h"
2528
#include "list.h"
@@ -55,6 +58,10 @@ Context *context_new(GlobalContext *glb)
5558

5659
context_clean_registers(ctx, 0);
5760

61+
#ifndef AVM_NO_FP
62+
ctx->fr = NULL;
63+
#endif
64+
5865
ctx->min_heap_size = 0;
5966
ctx->max_heap_size = 0;
6067
ctx->has_min_heap_size = 0;
@@ -111,6 +118,11 @@ Context *context_new(GlobalContext *glb)
111118

112119
void context_destroy(Context *ctx)
113120
{
121+
#ifndef AVM_NO_FP
122+
if (ctx->fr) {
123+
free(ctx->fr);
124+
}
125+
#endif
114126
list_remove(&ctx->processes_table_head);
115127

116128
memory_sweep_mso_list(ctx->mso_list);

src/libAtomVM/context.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ struct Context
7171

7272
term x[MAX_REG];
7373

74+
#ifndef AVM_NO_FP
75+
avm_float_t *fr;
76+
#endif
77+
7478
term *heap_start;
7579
term *stack_base;
7680
term *heap_ptr;
@@ -163,6 +167,23 @@ Context *context_new(GlobalContext *glb);
163167
*/
164168
void context_destroy(Context *c);
165169

170+
#ifndef AVM_NO_FP
171+
/**
172+
* @brief Ensure we have FP registers, allocating them if necessary.
173+
* @param c context fo allocate FP registers for
174+
*/
175+
static inline void context_ensure_fpregs(Context *c)
176+
{
177+
if (UNLIKELY(c->fr == NULL)) {
178+
c->fr = (avm_float_t *) malloc(sizeof(avm_float_t) * MAX_REG);
179+
if (UNLIKELY(c->fr == NULL)) {
180+
fprintf(stderr, "Could not allocate FP registers\n");
181+
AVM_ABORT();
182+
}
183+
}
184+
}
185+
#endif
186+
166187
/**
167188
* @brief Starts executing a function
168189
*

src/libAtomVM/opcodes.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@
8787
#define OP_BS_PUT_INTEGER 89
8888
#define OP_BS_PUT_BINARY 90
8989
#define OP_BS_PUT_STRING 92
90+
#define OP_FCLEARERROR 94
91+
#define OP_FCHECKERROR 95
92+
#define OP_FMOVE 96
93+
#define OP_FCONV 97
94+
#define OP_FADD 98
95+
#define OP_FSUB 99
96+
#define OP_FMUL 100
97+
#define OP_FDIV 101
98+
#define OP_FNEGATE 102
9099
#define OP_MAKE_FUN2 103
91100
#define OP_TRY 104
92101
#define OP_TRY_END 105

src/libAtomVM/opcodesswitch.h

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5100,6 +5100,272 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
51005100
break;
51015101
}
51025102

5103+
#ifndef AVM_NO_FP
5104+
case OP_FCLEARERROR: {
5105+
// This can be a noop as we raise from bifs
5106+
TRACE("fclearerror/0\n");
5107+
NEXT_INSTRUCTION(1);
5108+
break;
5109+
}
5110+
5111+
case OP_FCHECKERROR: {
5112+
int next_off = 1;
5113+
// This can be a noop as we raise from bifs
5114+
int fail_label;
5115+
DECODE_LABEL(fail_label, code, i, next_off);
5116+
NEXT_INSTRUCTION(next_off);
5117+
break;
5118+
}
5119+
5120+
case OP_FMOVE: {
5121+
int next_off = 1;
5122+
if (IS_EXTENDED_FP_REGISTER(code, i, next_off)) {
5123+
int freg;
5124+
DECODE_FP_REGISTER(freg, code, i, next_off);
5125+
dreg_t dreg;
5126+
dreg_type_t dreg_type;
5127+
DECODE_DEST_REGISTER(dreg, dreg_type, code, i, next_off);
5128+
#ifdef IMPL_EXECUTE_LOOP
5129+
TRACE("fmove/2 fp%i, %c%i\n", freg, T_DEST_REG(dreg_type, dreg));
5130+
// Space should be available on heap as compiler added an allocate opcode
5131+
context_ensure_fpregs(ctx);
5132+
term float_value = term_from_float(ctx->fr[freg], ctx);
5133+
WRITE_REGISTER(dreg_type, dreg, float_value);
5134+
#endif
5135+
#ifdef IMPL_CODE_LOADER
5136+
TRACE("fmove/2\n");
5137+
UNUSED(freg)
5138+
UNUSED(dreg)
5139+
UNUSED(dreg_type)
5140+
#endif
5141+
} else {
5142+
term src_value;
5143+
DECODE_COMPACT_TERM(src_value, code, i, next_off);
5144+
int freg;
5145+
DECODE_FP_REGISTER(freg, code, i, next_off);
5146+
#ifdef IMPL_EXECUTE_LOOP
5147+
TRACE("fmove/2 %lx, fp%i\n", src_value, freg);
5148+
context_ensure_fpregs(ctx);
5149+
ctx->fr[freg] = term_to_float(src_value);
5150+
#endif
5151+
#ifdef IMPL_CODE_LOADER
5152+
TRACE("fmove/2\n");
5153+
UNUSED(src_value)
5154+
UNUSED(freg)
5155+
#endif
5156+
}
5157+
5158+
NEXT_INSTRUCTION(next_off);
5159+
break;
5160+
}
5161+
5162+
case OP_FCONV: {
5163+
int next_off = 1;
5164+
term src_value;
5165+
DECODE_COMPACT_TERM(src_value, code, i, next_off);
5166+
int freg;
5167+
DECODE_FP_REGISTER(freg, code, i, next_off);
5168+
5169+
#ifdef IMPL_EXECUTE_LOOP
5170+
TRACE("fconv/2 %lx, fp%i\n", src_value, freg);
5171+
context_ensure_fpregs(ctx);
5172+
ctx->fr[freg] = term_conv_to_float(src_value);
5173+
#endif
5174+
5175+
#ifdef IMPL_CODE_LOADER
5176+
TRACE("fconv/2\n");
5177+
UNUSED(freg)
5178+
UNUSED(src_value)
5179+
#endif
5180+
5181+
NEXT_INSTRUCTION(next_off);
5182+
break;
5183+
}
5184+
5185+
case OP_FADD: {
5186+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5187+
#pragma STDC FENV_ACCESS ON
5188+
#endif
5189+
int next_off = 1;
5190+
int fail_label;
5191+
DECODE_LABEL(fail_label, code, i, next_off);
5192+
int freg1, freg2, freg3;
5193+
DECODE_FP_REGISTER(freg1, code, i, next_off);
5194+
DECODE_FP_REGISTER(freg2, code, i, next_off);
5195+
DECODE_FP_REGISTER(freg3, code, i, next_off);
5196+
#ifdef IMPL_EXECUTE_LOOP
5197+
TRACE("fadd/3 fp%i, fp%i, fp%i\n", freg1, freg2, freg3);
5198+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5199+
feclearexcept(FE_OVERFLOW);
5200+
#endif
5201+
context_ensure_fpregs(ctx);
5202+
ctx->fr[freg3] = ctx->fr[freg1] + ctx->fr[freg2];
5203+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5204+
if (fetestexcept(FE_OVERFLOW)) {
5205+
RAISE_ERROR(BADARITH_ATOM);
5206+
}
5207+
#else
5208+
if (!isfinite(ctx->fr[freg3])) {
5209+
RAISE_ERROR(BADARITH_ATOM);
5210+
}
5211+
#endif
5212+
#endif
5213+
5214+
#ifdef IMPL_CODE_LOADER
5215+
TRACE("fadd/3\n");
5216+
UNUSED(freg1)
5217+
UNUSED(freg2)
5218+
UNUSED(freg3)
5219+
#endif
5220+
5221+
NEXT_INSTRUCTION(next_off);
5222+
break;
5223+
}
5224+
5225+
case OP_FSUB: {
5226+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5227+
#pragma STDC FENV_ACCESS ON
5228+
#endif
5229+
int next_off = 1;
5230+
int fail_label;
5231+
DECODE_LABEL(fail_label, code, i, next_off);
5232+
int freg1, freg2, freg3;
5233+
DECODE_FP_REGISTER(freg1, code, i, next_off);
5234+
DECODE_FP_REGISTER(freg2, code, i, next_off);
5235+
DECODE_FP_REGISTER(freg3, code, i, next_off);
5236+
#ifdef IMPL_EXECUTE_LOOP
5237+
TRACE("fsub/3 fp%i, fp%i, fp%i\n", freg1, freg2, freg3);
5238+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5239+
feclearexcept(FE_OVERFLOW);
5240+
#endif
5241+
context_ensure_fpregs(ctx);
5242+
ctx->fr[freg3] = ctx->fr[freg1] - ctx->fr[freg2];
5243+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5244+
if (fetestexcept(FE_OVERFLOW)) {
5245+
RAISE_ERROR(BADARITH_ATOM);
5246+
}
5247+
#else
5248+
if (!isfinite(ctx->fr[freg3])) {
5249+
RAISE_ERROR(BADARITH_ATOM);
5250+
}
5251+
#endif
5252+
#endif
5253+
5254+
#ifdef IMPL_CODE_LOADER
5255+
TRACE("fsub/3\n");
5256+
UNUSED(freg1)
5257+
UNUSED(freg2)
5258+
UNUSED(freg3)
5259+
#endif
5260+
5261+
NEXT_INSTRUCTION(next_off);
5262+
break;
5263+
}
5264+
5265+
case OP_FMUL: {
5266+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5267+
#pragma STDC FENV_ACCESS ON
5268+
#endif
5269+
int next_off = 1;
5270+
int fail_label;
5271+
DECODE_LABEL(fail_label, code, i, next_off);
5272+
int freg1, freg2, freg3;
5273+
DECODE_FP_REGISTER(freg1, code, i, next_off);
5274+
DECODE_FP_REGISTER(freg2, code, i, next_off);
5275+
DECODE_FP_REGISTER(freg3, code, i, next_off);
5276+
#ifdef IMPL_EXECUTE_LOOP
5277+
TRACE("fmul/3 fp%i, fp%i, fp%i\n", freg1, freg2, freg3);
5278+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5279+
feclearexcept(FE_OVERFLOW);
5280+
#endif
5281+
context_ensure_fpregs(ctx);
5282+
ctx->fr[freg3] = ctx->fr[freg1] * ctx->fr[freg2];
5283+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5284+
if (fetestexcept(FE_OVERFLOW)) {
5285+
RAISE_ERROR(BADARITH_ATOM);
5286+
}
5287+
#else
5288+
if (!isfinite(ctx->fr[freg3])) {
5289+
RAISE_ERROR(BADARITH_ATOM);
5290+
}
5291+
#endif
5292+
#endif
5293+
5294+
#ifdef IMPL_CODE_LOADER
5295+
TRACE("fmul/3\n");
5296+
UNUSED(freg1)
5297+
UNUSED(freg2)
5298+
UNUSED(freg3)
5299+
#endif
5300+
5301+
NEXT_INSTRUCTION(next_off);
5302+
break;
5303+
}
5304+
5305+
case OP_FDIV: {
5306+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5307+
#pragma STDC FENV_ACCESS ON
5308+
#endif
5309+
int next_off = 1;
5310+
int fail_label;
5311+
DECODE_LABEL(fail_label, code, i, next_off);
5312+
int freg1, freg2, freg3;
5313+
DECODE_FP_REGISTER(freg1, code, i, next_off);
5314+
DECODE_FP_REGISTER(freg2, code, i, next_off);
5315+
DECODE_FP_REGISTER(freg3, code, i, next_off);
5316+
#ifdef IMPL_EXECUTE_LOOP
5317+
TRACE("fdiv/3 fp%i, fp%i, fp%i\n", freg1, freg2, freg3);
5318+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5319+
feclearexcept(FE_OVERFLOW | FE_DIVBYZERO);
5320+
#endif
5321+
context_ensure_fpregs(ctx);
5322+
ctx->fr[freg3] = ctx->fr[freg1] / ctx->fr[freg2];
5323+
#ifdef HAVE_PRAGMA_STDC_FENV_ACCESS
5324+
if (fetestexcept(FE_OVERFLOW | FE_DIVBYZERO)) {
5325+
RAISE_ERROR(BADARITH_ATOM);
5326+
}
5327+
#else
5328+
if (!isfinite(ctx->fr[freg3])) {
5329+
RAISE_ERROR(BADARITH_ATOM);
5330+
}
5331+
#endif
5332+
#endif
5333+
5334+
#ifdef IMPL_CODE_LOADER
5335+
TRACE("fdiv/3\n");
5336+
UNUSED(freg1)
5337+
UNUSED(freg2)
5338+
UNUSED(freg3)
5339+
#endif
5340+
5341+
NEXT_INSTRUCTION(next_off);
5342+
break;
5343+
}
5344+
5345+
case OP_FNEGATE: {
5346+
int next_off = 1;
5347+
int fail_label;
5348+
DECODE_LABEL(fail_label, code, i, next_off);
5349+
int freg1, freg2;
5350+
DECODE_FP_REGISTER(freg1, code, i, next_off);
5351+
DECODE_FP_REGISTER(freg2, code, i, next_off);
5352+
#ifdef IMPL_EXECUTE_LOOP
5353+
TRACE("fnegate/2 fp%i, fp%i\n", freg1, freg2);
5354+
context_ensure_fpregs(ctx);
5355+
ctx->fr[freg2] = - ctx->fr[freg1];
5356+
#endif
5357+
5358+
#ifdef IMPL_CODE_LOADER
5359+
TRACE("fnegate/2\n");
5360+
UNUSED(freg1)
5361+
UNUSED(freg2)
5362+
#endif
5363+
5364+
NEXT_INSTRUCTION(next_off);
5365+
break;
5366+
}
5367+
#endif
5368+
51035369
#ifdef ENABLE_OTP21
51045370
case OP_GET_HD: {
51055371
int next_off = 1;

tests/erlang_tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ compile_erlang(floatmul)
345345
compile_erlang(floatmulovf)
346346
compile_erlang(floatneg)
347347
compile_erlang(floatabs)
348+
compile_erlang(floatdiv)
348349
compile_erlang(floatmath)
349350

350351
compile_erlang(boxed_is_not_float)
@@ -751,6 +752,7 @@ add_custom_target(erlang_test_modules DEPENDS
751752
floatmulovf.beam
752753
floatneg.beam
753754
floatabs.beam
755+
floatdiv.beam
754756
floatmath.beam
755757

756758
boxed_is_not_float.beam

0 commit comments

Comments
 (0)