[erlang-questions] Half word emulators and NIFs

Sverker Eriksson <>
Thu Jun 30 16:27:38 CEST 2011


Sverker Eriksson wrote:
> Paul Davis wrote:
>> On Mon, Jun 27, 2011 at 6:03 AM, Sverker Eriksson
>> <> wrote:
>>  
>>> Have you tested with a debug built emulator. That may catch faulty NIF
>>> behaviors earlier. Otherwise if a NIF builds broken terms on the 
>>> process
>>> heap it might not be discovered until later when those terms are 
>>> matched or
>>> maybe garbage collected.
>>>
>>> There is no difference to make NIF's for the halfword emulator. Just 
>>> use the
>>> NIF API as intended and don't cheat (make assumptions about
>>> sizeof(ERL_NIF_TERM) for example).
>>>
>>> /Sverker, Erlang/OTP
>>>
>>>     
>>
>> I've reduced this to a failing test case in the NIF API [1].
>>
>> In the end it boils down to these two functions (notice one is int64,
>> the other not):
>>
>> ERL_NIF_TERM
>> run_test1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
>> {
>>     return enif_make_int64(env, 1);
>> }
>>
>> ERL_NIF_TERM
>> run_test2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
>> {
>>     return enif_make_int(env, 1);
>> }
>>
>> Test 1 returns a Value V where (V =:= 1) is false, but (V == 1) is
>> true. The second function returns a value where both comparisons are
>> true.
>>
>> Paolo also found that amusingly this bug disappears at 2^27 as shown by:
>>
>> ERL_NIF_TERM
>> run_test3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
>> {
>>     return enif_make_int64(env, 134217727);
>> }
>>
>> ERL_NIF_TERM
>> run_test4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
>> {
>>     return enif_make_int64(env, 134217728);
>> }
>>
>> run_test3 returns a value that can't be pattern matched against
>> 134217727 but test 4 returns a value that can be matched against
>> 134217728.
>>
>> Also, I built R14B03 on Ubuntu 10.04 with this configure:
>>
>> ./configure --enable-smp --enable-threads --enable-m64-build
>> --without-javac --enable-halfword-emulator
>>
>> The processors are a pair of dual core Xeon's if that's important at 
>> all.
>>
>> [1] https://github.com/davisp/halfwordtest
>>
>>   
> Very nice trouble shooting. Thank you Paul.
>
> It's a halfword-bug affecting:
> enif_make_int64
> enif_make_long
> enif_make_uint64
> enif_make_ulong
>
> for integer values that can fit into 28 bits.
>
> What happens is that it makes a bignum even though the value could have
> fit into a small integer. Small bignums like that are not allowed 
> internally
> and that's the reason why it behaves so strangely when comparing.
>
> I'll be back with a source patch.
>
> /Sverker, Erlang/OTP
>
Here is a source patch. Only tested on halfword emulator.

diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 68421b4..d9b1a8e 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -833,8 +833,11 @@ ERL_NIF_TERM enif_make_uint(ErlNifEnv* env, unsigned i)

 ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i)
 {
+    if (IS_SSMALL(i)) {
+       return make_small(i);
+    }
 #if SIZEOF_LONG == ERTS_SIZEOF_ETERM
-    return IS_SSMALL(i) ? make_small(i) : small_to_big(i, 
alloc_heap(env,2));
+    return small_to_big(i, alloc_heap(env,2));
 #elif SIZEOF_LONG == 8
     ensure_heap(env,3);
     return erts_sint64_to_big(i, &env->hp);
@@ -843,8 +846,11 @@ ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i)

 ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i)
 {
+    if (IS_USMALL(0,i)) {
+       return make_small(i);
+    }
 #if SIZEOF_LONG == ERTS_SIZEOF_ETERM
-    return IS_USMALL(0,i) ? make_small(i) : 
uint_to_big(i,alloc_heap(env,2));
+    return uint_to_big(i,alloc_heap(env,2));
 #elif SIZEOF_LONG == 8
     ensure_heap(env,3);
     return erts_uint64_to_big(i, &env->hp);



/Sverker, Erlang/OTP




More information about the erlang-questions mailing list