LLVM build, problems passing string to LLVMSetValueName C API - c

Having successfully built LLVM using MinGW I am now trying to use the C API to implement the program.
As just a starter application to see if the build has been successful I have converted the llvmpy example found here http://www.llvmpy.org/llvmpy-doc/0.9/doc/firstexample.html into (what I think is the) C equivalent however I'm not getting the output I expect from the print function.
My C program is:
#include "llvm-c/Core.h"
#include "stdio.h"
int main(int argc, char* argv[])
{
LLVMInitializeCore(LLVMGetGlobalPassRegistry());
LLVMModuleRef my_module = LLVMModuleCreateWithName("my_module");
LLVMTypeRef ty_int = LLVMInt32Type();
LLVMTypeRef* ParamTypes = new LLVMTypeRef[2];
ParamTypes[0] = ty_int;
ParamTypes[1] = ty_int;
LLVMTypeRef ty_func = LLVMFunctionType(ty_int, ParamTypes, 2, false);
delete[] ParamTypes;
LLVMValueRef f_sum = LLVMAddFunction(my_module, "sum", ty_func);
LLVMValueRef* Params = new LLVMValueRef[2];
LLVMGetParams(f_sum, Params);
LLVMSetValueName(Params[0], "a");
LLVMSetValueName(Params[1], "b");
LLVMBasicBlockRef bb = LLVMAppendBasicBlock(f_sum, "entry");
LLVMBuilderRef builder = LLVMCreateBuilder();
LLVMPositionBuilderAtEnd(builder, bb);
LLVMValueRef tmp = LLVMBuildAdd(builder, Params[0], Params[1], "tmp");
delete[] Params;
LLVMBuildRet(builder, tmp);
printf(LLVMPrintModuleToString(my_module));
//do shutdown
LLVMDisposeBuilder(builder);
LLVMDisposeModule(my_module);
LLVMShutdown();
return 0;
}
The output I get is:
; ModuleID = 'my_module'
define i32 #sum(i32 0x1.74bb00p-1012, i32 b) {
entry:
tmp = add i32 0x1.95bc40p+876, b
ret i32 tmp
}
Note that 0x1.74bb00p-1012 and 0x1.95bc40p+876 should read "%a"
I can only think that is some kind of memory corruption however I don't know the likely cause. How could I change the code so this works?

As it turns out this is a problem with LLVMPrintModuleToStringNw, it uses the C printf function to print to the string and so the percentages are either removed or spew false values out of whatever is on the stack.
See for why %a came out as "0x1.74bb00p-1012" -Aka hexadecimal floating point format.
http://www.cplusplus.com/reference/cstdio/printf/
In the end LLVMPrintModuleToString should be replaced with a function that doesn't use the C print family of functions.

Related

How to convert a int to float in LLVM C? [duplicate]

I have ConstantInt and ConstantFP values that I want to add using fadd. However, I'm having trouble casting the ConstantInt into a floating point number that fadd will accept.
Here is an excerpt of the code:
Value* left = ConstantInt::get(Type::getInt64Ty(getGlobalContext()), 12, true);
Value* right = ConstantFP::get(Type::getFloatTy(getGlobalContext()), 11.6);
Instruction* cast = CastInst::Create(Instruction::SIToFP, left, left->getType(), "", currentBlock());
left = cast->getOperand(0);
BinaryOperator::Create(Instruction::FAdd, left, right, "", currentBlock());
where currentBlock() returns a BasicBlock. After trying to generate the opcode for this, LLVM complains that it can't add the two values because they are not the same.
I'm rather new to LLVM, so I'll take any advice if this code makes no sense.
My usual approach with these things is see what Clang generates - both the LLVM IR and the C++ API calls (C++ backend). You can use the online instance for simplicity. So, compiling this C code:
float foo(int a, float b) {
return a + b;
}
Gives me this LLVM IR:
define float #foo(i32 %a, float %b) #0 {
entry:
%conv = sitofp i32 %a to float
%add = fadd float %conv, %b
ret float %add
}
And this is the C++ API calls required to recreate that:
// Function: foo (func_foo)
{
Function::arg_iterator args = func_foo->arg_begin();
Value* int32_a = args++;
int32_a->setName("a");
Value* float_b = args++;
float_b->setName("b");
BasicBlock* label_entry = BasicBlock::Create(mod->getContext(), "entry",func_foo,0);
// Block entry (label_entry)
CastInst* float_conv = new SIToFPInst(int32_a, Type::getFloatTy(mod->getContext()), "conv", label_entry);
BinaryOperator* float_add = BinaryOperator::Create(Instruction::FAdd, float_conv, float_b, "add", label_entry);
ReturnInst::Create(mod->getContext(), float_add, label_entry);
}
You're free to tweak the input C code (i.e. replacing vars with constants, etc) and seeing what Clang/LLVM emit. This is the best/quickest way to find your way around the IR and API when you're not too familiar with it.
The problem is here:
Instruction* cast = CastInst::Create(Instruction::SIToFP, left, left->getType(), "", currentBlock());
You casted left to left->getType(), i.e., you did nothing. Cast to right->getType() instead:
Instruction* cast = CastInst::Create(Instruction::SIToFP, left, right->getType(), "", currentBlock());
LLVM 12
Value *cg_binary(BinaryAst *ast)
{
auto l = codegen(ast->left);
auto r = codegen(ast->right);
switch (ast->op)
{
case parser::token::PLUS:
{
if (l->getType()->getTypeID() == Type::TypeID::DoubleTyID || r->getType()->getTypeID() == Type::TypeID::DoubleTyID)
return b->CreateFAdd(b->CreateSIToFP(l, b->getDoubleTy()), b->CreateSIToFP(r, b->getDoubleTy()));
else
return b->CreateAdd(l, r);
}
}
}

How to use inline assembly using the llvm-c API

I can not figure out how to piece everything together.
I want to generate llvm-ir for the following "C" instruction:
asm volatile("nop");
In the future I would like to include more advanced inline assembly, but this will be a good start.
I have read:
https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
https://llvm.org/docs/LangRef.html#inline-assembler-expressions
My last attempt is the following:
char myasm[256]="nop";
char myconstraint[256]={0};
LLVMTypeRef voidty=LLVMVoidType();
LLVMValueRef asmcall=LLVMGetInlineAsm(voidty,myasm,strlen(myasm),
myconstraint,strlen(myconstraint),1,1,LLVMInlineAsmDialectIntel);
LLVMSetVolatile(asmcall,1);
LLVMValueRef call = LLVMBuildCall( llbuilder, asmcall, (LLVMValueRef *)dummy->data,
dummy->size, "acall");
I just get a segfault. The problem is not the "dummy" array. I use it in other places without any problems. In this case it is just an empty list of 0 size.
Help would be appreciated.
The missing ingredient was:
LLVMTypeRef functy = LLVMFunctionType(voidty, (LLVMTypeRef *)ptypes->data, ptypes->size, 0);
Whole answer is therefore:
char myasm[256]="nop";
char myconstraint[256]={0};
LLVMTypeRef voidty = LLVMVoidType();
LLVMTypeRef functy = LLVMFunctionType(voidty, (LLVMTypeRef *)ptypes->data, ptypes->size, 0);
LLVMValueRef asmcall = LLVMGetInlineAsm(functy,myasm,strlen(myasm),
myconstraint,strlen(myconstraint),1,1,LLVMInlineAsmDialectIntel);
LLVMValueRef call = LLVMBuildCall2( llbuilder, functy, asmcall,
(LLVMValueRef *)pvalues->data,pvalues->size, "");

R Package: Call C Function Within Rcpp

I am writing an R package that contains C and Rcpp. The goal is to call the C function from R and within Rcpp, eventually performing most of the analysis in Rcpp and only returning to R for minimal tasks. My package compiles and calling my function from R works fine.
#generate some matrix. Numeric is fine too. Must have column names, no row names
myMat <- matrix(data = 1:100, nrow = 10, ncol = 10,
dimnames = list(NULL, LETTERS[1:10]))
#This works. Put in full path, no expansion. It returns null to the console.
MinimalExample::WriteMat(mat = myMat, file = "Full_Path_Please/IWork.csv",
sep = "," ,eol = "\n", dec = ".", buffMB = 8L)
However, attempting the same thing in Rcpp produces a SIGSEV error. I think the problem is how I am passing arguments to the function, but I can't figure out the proper way.
#include <Rcpp.h>
using namespace Rcpp;
extern "C"{
#include "fwrite.h"
}
//' #export
// [[Rcpp::export]]
void WriteMatCpp(String& fileName, NumericMatrix& testMat){
Rcpp::Rcout<<"I did start!"<<std::endl;
String patchName = fileName;
int whichRow = 1;
std::string newString = std::string(3 - toString(whichRow).length(), '0')
+ toString(whichRow);
patchName.replace_last(".csv", newString+".csv");
//Set objects to pass to print function
String comma = ",";
String eol = "\n";
String dot = ".";
int buffMem = 8;
//This is where I crash, giving a SIGSEV error
fwriteMain(testMat, (SEXP)&patchName, (SEXP)&comma, (SEXP)&eol,
(SEXP)&dot, (SEXP)&buffMem);
}
Here is a link to the GitHub repository with the package. https://github.com/GilChrist19/MinimalExample
Your call from C++ to C is wrong. You can't just write (SEXP)& in front of an arbitrary data structure and hope for it to become a SEXP.
Fix
Use a line such as this to convert what you have in C++ to the SEXP your C function expects using Rcpp::wrap() on each argument:
//This is where I crash, giving a SIGSEV error
fwriteMain(wrap(testMat), wrap(patchName), wrap(comma),
wrap(eol), wrap(dot), wrap(buffMem));
Demo
edd#brad:/tmp/MinimalExample/MinEx(master)$ Rscript RunMe.R
I did start!
edd#brad:/tmp/MinimalExample/MinEx(master)$ cat /tmp/IDoNotWork.csv
A,B,C,D,E,F,G,H,I,J
1,11,21,31,41,51,61,71,81,91
2,12,22,32,42,52,62,72,82,92
3,13,23,33,43,53,63,73,83,93
4,14,24,34,44,54,64,74,84,94
5,15,25,35,45,55,65,75,85,95
6,16,26,36,46,56,66,76,86,96
7,17,27,37,47,57,67,77,87,97
8,18,28,38,48,58,68,78,88,98
9,19,29,39,49,59,69,79,89,99
10,20,30,40,50,60,70,80,90,100
edd#brad:/tmp/MinimalExample/MinEx(master)$
See https://github.com/GilChrist19/MinimalExample/tree/master/MinEx for a complete example.

Call Racket function from C

I have a Racket module hw.rkt:
#lang racket/base
(provide hw)
(define (hw) (displayln "Hello, world!"))
I would like to write a C program that embeds the Racket runtime and applies the procedure (hw).
There is example code here which demonstrates how to embed the Racket runtime and apply a procedure that is in racket/base, or to read and evaluate an S-expression, but I've had no luck modifying this code to allow access to the (hw) procedure.
This page seems to say that it is possible to do what I want to do by first compiling hw.rkt to hw.c using raco ctool --c-mods, and this works just fine when I try it, but I still can't actually access the (hw) procedure.
If someone could post a complete example program, or simply describe which C functions to use, I would be very appreciative. From there I can figure out the rest.
Editing to provide examples of things I have tried.
I modified the example program to get rid of the "evaluate command line arguments" bit and skip straight to the REPL so that I could experiment. Thus (with "hw.c" the result of running raco ctool --c-mods hw.c ++libs racket/base hw.rkt):
#define MZ_PRECISE_GC
#include "scheme.h"
#include "hw.c"
static int run(Scheme_Env *e, int argc, char *argv[])
{
Scheme_Object *curout = NULL, *v = NULL, *a[2] = {NULL, NULL};
Scheme_Config *config = NULL;
int i;
mz_jmp_buf * volatile save = NULL, fresh;
MZ_GC_DECL_REG(8);
MZ_GC_VAR_IN_REG(0, e);
MZ_GC_VAR_IN_REG(1, curout);
MZ_GC_VAR_IN_REG(2, save);
MZ_GC_VAR_IN_REG(3, config);
MZ_GC_VAR_IN_REG(4, v);
MZ_GC_ARRAY_VAR_IN_REG(5, a, 2);
MZ_GC_REG();
declare_modules(e);
v = scheme_intern_symbol("racket/base");
scheme_namespace_require(v);
config = scheme_current_config();
curout = scheme_get_param(config, MZCONFIG_OUTPUT_PORT);
save = scheme_current_thread->error_buf;
scheme_current_thread->error_buf = &fresh;
if (scheme_setjmp(scheme_error_buf)) {
scheme_current_thread->error_buf = save;
return -1; /* There was an error */
} else {
/* read-eval-print loop, uses initial Scheme_Env: */
a[0] = scheme_intern_symbol("racket/base");
a[1] = scheme_intern_symbol("read-eval-print-loop");
v = scheme_dynamic_require(2, a);
scheme_apply(v, 0, NULL);
scheme_current_thread->error_buf = save;
}
MZ_GC_UNREG();
return 0;
}
int main(int argc, char *argv[])
{
return scheme_main_setup(1, run, argc, argv);
}
Things that don't work (and their error messages):
Calling (hw) from the REPL
hw: undefined:
cannot reference undefined identifier
context...:
/usr/local/share/racket/collects/racket/private/misc.rkt:87:7
((dynamic-require 'hw 'hw))
standard-module-name-resolver: collection not found
for module path: hw
collection: "hw"
in collection directories:
context...:
show-collection-err
standard-module-name-resolver
/usr/local/share/racket/collects/racket/private/misc.rkt:87:7
((dynamic-require "hw.rkt" 'hw))
standard-module-name-resolver: collection not found
for module path: racket/base/lang/reader
collection: "racket/base/lang"
in collection directories:
context...:
show-collection-err
standard-module-name-resolver
standard-module-name-resolver
/usr/local/share/racket/collects/racket/private/misc.rkt:87:7
Editing the example code
v = scheme_intern_symbol("racket/base");
scheme_namespace_require(v);
v = scheme_intern_symbol("hw");
scheme_namespace_require(v);
Error:
standard-module-name-resolver: collection not found
for module path: hw
collection: "hw"
in collection directories:
context...:
show-collection-err
standard-module-name-resolver
SIGSEGV MAPERR sicode 1 fault on addr 0xd0
Aborted
(The segfault was probably because I didn't check the value of 'v' before trying to scheme_namespace_require it.)
Editing the example code mk. 2
v = scheme_intern_symbol("racket/base");
scheme_namespace_require(v);
v = scheme_intern_symbol("hw.rkt");
scheme_namespace_require(v);
Error:
hw.rkt: bad module path
in: hw.rkt
context...:
standard-module-name-resolver
SIGSEGV MAPERR sicode 1 fault on addr 0xd0
Aborted
(re: segfault: as above)
Editing the example code mk. 3
v = scheme_intern_symbol("racket/base");
scheme_namespace_require(v);
v = scheme_intern_symbol("./hw.rkt");
scheme_namespace_require(v);
(as above)
Editing the example code mk. 4
/* read-eval-print-loop, uses initial Scheme_Env: */
a[0] = scheme_intern_symbol("hw");
a[1] = scheme_intern_symbol("hw");
v = scheme_dynamic_require(2, a);
(as mk. 1, save the segfault)
Editing the example code mk. 5
/* read-eval-print loop, uses initial Scheme_Env: */
a[0] = scheme_intern_symbol("hw");
a[1] = scheme_eval(a[0], e);
scheme_apply(a[1], 0, NULL);
Error:
hw: undefined;
cannot reference undefined identifier
Answered by Matthew Flatt here. When using dynamic-require, I needed to quote the name of the module twice, not once. Thanks to Dr. Flatt for their assistance.

creating va_list dynamically in GCC - can it be done?

my problem with vsprintf is that I can not obtain input arguments directly, I have to first get inputs one by one and save them in void**, then pass this void** to vsprintf(), it is all fine for windows, but when I come to 64bit linux, gcc cannot compile because it is not allowed to convert from void** to va_list, Is there anyone that can give me some help how I should do this under linux?
Can I create va_list dynamically in GCC?
void getInputArgs(char* str, char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
}
void process(void)
{
char s[256];
double tempValue;
char * tempString = NULL;
void ** args_ptr = NULL;
ArgFormatType format; //defined in the lib I used in the code
int numOfArgs = GetNumInputArgs(); // library func used in my code
if(numOfArgs>1)
{
args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
for(i=2; i<numOfArgs; i++)
{
format = GetArgType(); //library funcs
switch(format)
{
case ArgType_double:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
break;
case ArgType_char:
args_ptr[i-2]=NULL;
AllocInCharArg(i, TRUE, &tempString); //lib func
args_ptr[i-2]= tempString;
break;
}
}
}
getInputArgs(s, formatString, (va_list) args_ptr); //Here
// is the location where gcc cannot compile,
// Can I and how if I can create a va_list myself?
}
There is a way you can do this, but it is specific to gcc on Linux. It does work on Linux (tested) for both 32 and 64 bit builds.
DISCLAIMER: I am not endorsing using this code. It is not portable, it is hackish, and is quite frankly a precariously balanced elephant on a proverbial tightrope. I am merely demonstrating that it is possible to dynamically create a va_list using gcc, which is what the original question was asking.
With that said, the following article details how va_list works with the amd64 ABI: Amd64 and Va_arg.
With knowledge of the internal structure of the va_list struct, we can trick the va_arg macro into reading from a va_list that we construct ourselves:
#if (defined( __linux__) && defined(__x86_64__))
// AMD64 byte-aligns elements to 8 bytes
#define VLIST_CHUNK_SIZE 8
#else
#define VLIST_CHUNK_SIZE 4
#define _va_list_ptr _va_list
#endif
typedef struct {
va_list _va_list;
#if (defined( __linux__) && defined(__x86_64__))
void* _va_list_ptr;
#endif
} my_va_list;
void my_va_start(my_va_list* args, void* arg_list)
{
#if (defined(__linux__) && defined(__x86_64__))
/* va_args will read from the overflow area if the gp_offset
is greater than or equal to 48 (6 gp registers * 8 bytes/register)
and the fp_offset is greater than or equal to 304 (gp_offset +
16 fp registers * 16 bytes/register) */
args->_va_list[0].gp_offset = 48;
args->_va_list[0].fp_offset = 304;
args->_va_list[0].reg_save_area = NULL;
args->_va_list[0].overflow_arg_area = arg_list;
#endif
args->_va_list_ptr = arg_list;
}
void my_va_end(my_va_list* args)
{
free(args->_va_list_ptr);
}
typedef struct {
ArgFormatType type; // OP defined this enum for format
union {
int i;
// OTHER TYPES HERE
void* p;
} data;
} va_data;
Now, we can generate the va_list pointer (which is the same for both 64 bit and 32 bit builds) using something like your process() method or the following:
void* create_arg_pointer(va_data* arguments, unsigned int num_args) {
int i, arg_list_size = 0;
void* arg_list = NULL;
for (i=0; i < num_args; ++i)
{
unsigned int native_data_size, padded_size;
void *native_data, *vdata;
switch(arguments[i].type)
{
case ArgType_int:
native_data = &(arguments[i].data.i);
native_data_size = sizeof(arguments[i]->data.i);
break;
// OTHER TYPES HERE
case ArgType_string:
native_data = &(arguments[i].data.p);
native_data_size = sizeof(arguments[i]->data.p);
break;
default:
// error handling
continue;
}
// if needed, pad the size we will use for the argument in the va_list
for (padded_size = native_data_size; 0 != padded_size % VLIST_CHUNK_SIZE; padded_size++);
// reallocate more memory for the additional argument
arg_list = (char*)realloc(arg_list, arg_list_size + padded_size);
// save a pointer to the beginning of the free space for this argument
vdata = &(((char *)(arg_list))[arg_list_size]);
// increment the amount of allocated space (to provide the correct offset and size for next time)
arg_list_size += padded_size;
// set full padded length to 0 and copy the actual data into the location
memset(vdata, 0, padded_size);
memcpy(vdata, native_data, native_data_size);
}
return arg_list;
}
And finally, we can use it:
va_data data_args[2];
data_args[0].type = ArgType_int;
data_args[0].data.i = 42;
data_args[1].type = ArgType_string;
data_args[1].data.p = "hello world";
my_va_list args;
my_va_start(&args, create_arg_pointer(data_args, 2));
vprintf("format string %d %s", args._va_list);
my_va_end(&args);
And there you have it. It works mostly the same as the normal va_start and va_end macros, but lets you pass your own dynamically generated, byte-aligned pointer to be used instead of relying on the calling convention to set up your stack frame.
I have tried using libffi as mentioned somewhere else and it works.
Here below is the link , hope it can help others with similar issues.
Thanks again for all help I got here!
Link:
http://www.atmark-techno.com/~yashi/libffi.html -- simple example given
http://www.swig.org/Doc1.3/Varargs.html -- printf() and other examples given
The type of va_list is not void ** or anything similar with 64-bit gcc (on Intel x86/64 machines). On both Mac OS X 10.7.4 and on RHEL 5, there is no header stdarg.h in /usr/include. Consider the following code:
#include <stdarg.h>
#include <stdio.h>
int main(void)
{
printf("sizeof(va_list) = %zu\n", sizeof(va_list));
return 0;
}
The output on both RHEL 5 and Mac OS X 10.7 with a 64-bit compilation is:
sizeof(va_list) = 24
With a 32-bit compilation, the output on each platform is:
sizeof(va_list) = 4
(You may take it that I was surprised to find this much discrepancy between the 32-bit and 64-bit versions. I was expecting a value between 12 and 24 for the 32-bit version.)
So, the type is opaque; you can't even find a header that tells you anything about; and it is much bigger than a single pointer on 64-bit machines.
Even if your code works on some machines, it is very, very far from guaranteed to work everywhere.
The GCC 4.7.1 manual does not mention any functions that allow you to build a va_list at runtime.
Following class works for me:
class VaList
{
va_list _arguments;
public:
explicit inline VaList(const void * pDummy, ...)
{
va_start(_arguments, pDummy);
}
inline operator va_list &()
{
return _arguments;
}
inline operator const va_list &() const
{
return _arguments;
}
inline ~VaList()
{
va_end(_arguments);
}
};
and it can be used like this:
void v(const char * format, const va_list & arguments)
{
vprintf(format, const_cast<va_list &>(arguments));
}
...
v("%d\n", VaList("", 1)); // Uses VaList::operator va_list &()
v("%d %d\n", VaList(nullptr, 2, 3)); // Uses VaList::operator va_list &()
vprintf("%s %s %s\n", VaList("", "Works", "here", "too!"));
const VaList args(NULL, 4, 5, "howdy", "there");
v("%d %d %s %s\n", args); // Uses VaList::operator const va_list &() const
The first dummy parameter can be any kind of pointer, it is only used to compute the address of the following arguments.
The same can of course be done in C too but not so niftily (use pointer instead of reference)!
Simple example of using VaList to construct a dynamic va_list:
static void VectorToVaList(const std::vector<int> & v, va_list & t)
{
switch (v.size())
{
case 1: va_copy(t, VaList("", v[0])); return;
case 2: va_copy(t, VaList("", v[0], v[1])); return;
case 3: va_copy(t, VaList("", v[0], v[1], v[2])); return;
case 4: va_copy(t, VaList("", v[0], v[1], v[2], v[3])); return;
// etc
}
throw std::out_of_range("Out of range vector size!");
}
and usage:
va_list t;
VectorToVaList(std::vector<int>{ 1, 2, 3, 4 }, t);
vprintf("%d %d %d %d\n", t);
If the problem you're trying to solve is inserting passing arbitrary types to a function in va_list style, then, consider using union:
#include <iostream>
#include <cstdarg>
union ARG
{
int d;
char* s;
double f;
};
int main()
{
printf("%d %s %f \n", 1, "two", 3.1415 );
// Output: 1 two 3.141500
char format[ 1024 ] = "%d %s %f\n";
ARG args[ 5 ] = { };
args[ 0 ].d = 1;
args[ 1 ].s = "two";
args[ 2 ].f = 3.1415;
printf( format, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
// Output: 1 two 3.141500
return 0;
}
Some things you'll note about my solution:
No attempt is made to produce the correct number of arguments. i.e. I oversupply the arguments, but, most functions will look at the first parameter to determine how to handle the rest (i.e. format)
I didn't bother dynamically create the format, but, it is a trivial exercise to build a routine that dynamically populates format and args.
Tested this on:
- Ubuntu, g++
- Android NDK
I did some more testing, and, confirmed #PeterCoordes comments about this answer not working for double precision.

Resources