Skip to content

Commit

Permalink
correct the codegen of floating point subtypes
Browse files Browse the repository at this point in the history
avoids casting away floating point bits until used in a floating-point operation
also ensures that alignment is consistent in the LLVM struct representation as in the Julia struct representation computation

fix #21216
  • Loading branch information
vtjnash committed Apr 18, 2017
1 parent 2e89bc1 commit 9104459
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 42 deletions.
10 changes: 7 additions & 3 deletions doc/src/manual/embedding.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,17 @@ square root of 2 in Julia and reads back the result in C looks as follows:
```
jl_value_t *ret = jl_eval_string("sqrt(2.0)");
if (jl_is_float64(ret)) {
if (jl_typeis(ret, jl_float64_type)) {
double ret_unboxed = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e \n", ret_unboxed);
}
else {
printf("ERROR: unexpected return type from sqrt(::Float64)\n");
}
```

In order to check whether `ret` is of a specific Julia type, we can use the `jl_is_...` functions.
In order to check whether `ret` is of a specific Julia type, we can use the
`jl_isa`, `jl_typeis`, or `jl_is_...` functions.
By typing `typeof(sqrt(2.0))` into the Julia shell we can see that the return type is `Float64`
(`double` in C). To convert the boxed Julia value into a C double the `jl_unbox_float64` function
is used in the above code snippet.
Expand Down Expand Up @@ -387,7 +391,7 @@ When writing Julia callable functions, it might be necessary to validate argumen
to indicate errors. A typical type check looks like:

```
if (!jl_is_float64(val)) {
if (!jl_typeis(val, jl_float64_type)) {
jl_type_error(function_name, (jl_value_t*)jl_float64_type, val);
}
```
Expand Down
14 changes: 4 additions & 10 deletions examples/embedding/embedding.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ int main()
// Accessing the return value

jl_value_t *ret = jl_eval_string("sqrt(2.0)");

if (jl_is_float64(ret)) {
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
}
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
}

{
Expand All @@ -39,11 +36,8 @@ int main()
jl_function_t *func = jl_get_function(jl_base_module, "sqrt");
jl_value_t* argument = jl_box_float64(2.0);
jl_value_t* ret = jl_call1(func, argument);

if (jl_is_float64(ret)) {
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
}
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
}

{
Expand Down
1 change: 1 addition & 0 deletions src/abi_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Type *get_llvm_vectype(jl_datatype_t *dt) const
return lltype;
}

#define jl_is_floattype(v) jl_subtype(v,(jl_value_t*)jl_floatingpoint_type)
Type *get_llvm_fptype(jl_datatype_t *dt) const
{
// Assume jl_is_datatype(dt) && !jl_is_abstracttype(dt)
Expand Down
2 changes: 2 additions & 0 deletions src/abi_arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ bool needPassByRef(jl_datatype_t *dt, AttrBuilder &ab) override
return false;
}

#define jl_is_floattype(v) jl_subtype(v,(jl_value_t*)jl_floatingpoint_type)

Type *get_llvm_fptype(jl_datatype_t *dt) const
{
// Assume jl_is_datatype(dt) && !jl_is_abstracttype(dt)
Expand Down
17 changes: 4 additions & 13 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,26 +402,17 @@ static Type *bitstype_to_llvm(jl_value_t *bt)
return T_int8;
if (bt == (jl_value_t*)jl_long_type)
return T_size;
if (bt == (jl_value_t*)jl_float32_type)
return T_float32;
if (bt == (jl_value_t*)jl_float64_type)
return T_float64;
if (jl_is_cpointer_type(bt)) {
Type *lt = julia_type_to_llvm(jl_tparam0(bt));
if (lt == T_void)
return T_pint8;
return PointerType::get(lt, 0);
}
int nb = jl_datatype_size(bt);
if (jl_is_floattype(bt)) {
#ifndef DISABLE_FLOAT16
if (nb == 2)
return T_float16;
else
#endif
if (nb == 4)
return T_float32;
else if (nb == 8)
return T_float64;
else if (nb == 16)
return T_float128;
}
return Type::getIntNTy(jl_LLVMContext, nb * 8);
}

Expand Down
12 changes: 0 additions & 12 deletions src/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,22 +177,14 @@ static Constant *julia_const_to_llvm(void *ptr, jl_value_t *bt)
}
case 2: {
uint16_t data16 = *(uint16_t*)ptr;
#ifndef DISABLE_FLOAT16
if (jl_is_floattype(bt))
return ConstantFP::get(jl_LLVMContext, LLVM_FP(APFloat::IEEEhalf,APInt(16,data16)));
#endif
return ConstantInt::get(T_int16, data16);
}
case 4: {
uint32_t data32 = *(uint32_t*)ptr;
if (jl_is_floattype(bt))
return ConstantFP::get(jl_LLVMContext, LLVM_FP(APFloat::IEEEsingle,APInt(32,data32)));
return ConstantInt::get(T_int32, data32);
}
case 8: {
uint64_t data64 = *(uint64_t*)ptr;
if (jl_is_floattype(bt))
return ConstantFP::get(jl_LLVMContext, LLVM_FP(APFloat::IEEEdouble,APInt(64,data64)));
return ConstantInt::get(T_int64, data64);
}
default:
Expand All @@ -212,10 +204,6 @@ static Constant *julia_const_to_llvm(void *ptr, jl_value_t *bt)
else
#endif
val = APInt(8*nb, ArrayRef<uint64_t>(data, nw));
if (nb == 16 && jl_is_floattype(bt)) {
return ConstantFP::get(jl_LLVMContext,LLVM_FP(APFloat::IEEEquad,val));
// If we have a floating point type that's not hardware supported, just treat it like an integer for LLVM purposes
}
return ConstantInt::get(IntegerType::get(jl_LLVMContext,8*nb),val);
}
}
Expand Down
4 changes: 0 additions & 4 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -847,10 +847,6 @@ static inline uint32_t jl_fielddesc_size(int8_t fielddesc_type)
#define jl_is_uint16(v) jl_typeis(v,jl_uint16_type)
#define jl_is_uint32(v) jl_typeis(v,jl_uint32_type)
#define jl_is_uint64(v) jl_typeis(v,jl_uint64_type)
#define jl_is_float(v) jl_isa(v,(jl_value_t*)jl_floatingpoint_type)
#define jl_is_floattype(v) jl_subtype(v,(jl_value_t*)jl_floatingpoint_type)
#define jl_is_float32(v) jl_typeis(v,jl_float32_type)
#define jl_is_float64(v) jl_typeis(v,jl_float64_type)
#define jl_is_bool(v) jl_typeis(v,jl_bool_type)
#define jl_is_symbol(v) jl_typeis(v,jl_sym_type)
#define jl_is_ssavalue(v) jl_typeis(v,jl_ssavalue_type)
Expand Down
30 changes: 30 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4825,3 +4825,33 @@ f21271(x) = x::Tuple{Type{Int}, Type{Float64}}
bar21397(x::T) where {T} = T
foo21397(x) = bar21397(x)
@test foo21397(Tuple) == DataType

# issue 21216
primitive type FP128test <: AbstractFloat 128 end
struct FP128align <: AbstractFloat
i::Int # cause forced misalignment (to word-size)
fp::FP128test
end
let ni128 = sizeof(FP128test) ÷ sizeof(Int),
ns128 = sizeof(FP128align) ÷ sizeof(Int),
nbit = sizeof(Int) * 8,
arr = reinterpret(FP128align, collect(Int, 1:(2 * ns128))),
little,
expected
@test sizeof(FP128test) == 16
@test length(arr) == 2
@test arr[1].i == 1
@test arr[2].i == 1 + ns128
expected = UInt128(0)
for little in ni128:-1:1
little += 1
expected = (expected << nbit) + little
end
@test arr[1].fp == reinterpret(FP128test, expected)
expected = UInt128(0)
for little in ni128:-1:1
little += 1 + ns128
expected = (expected << nbit) + little
end
@test reinterpret(UInt128, arr[2].fp) == expected
end

0 comments on commit 9104459

Please sign in to comment.