Inlining code from distinct files in LLVM - c

I need to inline a couple of functions during runtime using LLVM. The complication is that such functions are defined in separate bitcode files.
During runtime, I need to generate code for a function such as
void snippet1(); //declaring that snippet1 and 2 are defined in snippet1.c and snippet2.c
void snippet2();
void combo12(){
snippet1();
snippet1();
snippet2();
snippet2();
}
From separate LLVM bitcode files compiled from combo12.c, snippet1.c and snippet2.c. The thing is, I need to inline all calls to snippet1 and snippet2 in combo12. I tried to do this using the following code (main.cpp):
OwningPtr<MemoryBuffer> MB, MB2, MB3;
Module *M1, *M2, *MC12, *MOUT;
LLVMContext Context;
std::string ErrorStr;
MemoryBuffer::getFile("snippet1.bc", MB);
M1 = ParseBitcodeFile(MB.get(), Context);
MemoryBuffer::getFile("snippet2.bc", MB2);
M2 = ParseBitcodeFile(MB2.get(), Context);
MemoryBuffer::getFile("combo12.bc", MB3);
MC12 = ParseBitcodeFile(MB3.get(), Context);
Linker* L;
L = new Linker("testprog", M1, 0);
L->setFlags(llvm::Linker::Verbose);
if (!(L->LinkInModule(M2, &ErrorStr)))
std::cout << ErrorStr;
if (!(L->LinkInModule(MC12, &ErrorStr)))
std::cout << ErrorStr;
MOUT = L->getModule();
MOUT->dump();
PassManager *PM;
PM = new PassManager();
PM->add(createInternalizePass(true));
PM->add(createAlwaysInlinerPass());
if (PM->run(*MOUT)){
std::cout << "\n\n\nCode was altered!\n\n\n" << std::endl;
MOUT->dump();
std::cout << "\n\n ALTERED BEAST \n\n" << std::endl;
}
snippet1.c:
//What this function does is irrelevant
#include "post_opt.h" //contains the struct exstr declaration
extern struct exstr a;
inline void snippet1() __attribute((always_inline));
void snippet1(){
int x, y;
a.b = 10;
x = 2;
if(x < a.a){
y = x + 1;
}
}
I compiled snippet1.c, snippet2.c and combo12.c using
clang -c -emit-llvm snippet1.c -o snippet1.bc -O0
clang -c -emit-llvm snippet2.c -o snippet2.bc -O0
clang -c -emit-llvm combo12.c -o combo12.bc -O0
And main.cpp with
clang++ -g main.cpp `llvm-config --cppflags --ldflags --libs --cppflags --ldflags --libs core jit native linker transformutils ipo bitreader` -O0 -o main
When I run ./main, It does not inline the snippet code, although I explicitly mark the function with the always_inline attribute, and use the AlwaysInline pass. It never prints ALTERED BEAST onscreen.
Why does this happen? I thought that, by linking all modules together and applying an IPO pass (AlwaysInline) this would work out ok.
Thanks for any insight!

Inlining occurs during compilation and not linking and inlining is only available with completly defined function at compilation time. So, it is impossible to inlining function from other files ('cause when compiling a file, other one are ignored). The only solution is to define function in every files which need inline thoses functions.

Related

Attribute for ignoring "too many arguments in call to 'func'"

I am creating a light test framework. For my local debugging in linux with gcc and clang, I do not get any complaints for mocking a function that has arguments, but mocking it with no arguments. eg.
add.c
int add(int a, int b) {
return a + b;
}
foo.c
#include "add.h"
int add_2(int a) {
return add(a, 2);
}
Now, in order to mock add. I simply created these macros.
testframework.h
#define DECLARE_MOCK(type, name) \
type __var_##name[255]; \
size_t __var_##name##_inc = 0; \
size_t __var_##name##_actual = 0; \
type name() { return (type)__var_##name[__var_##name##_inc++]; }
#define MOCK(name, value) __var_##name[__var_##name##_actual++] = value;
This works well on my linux machine. add(x,y) requires two arguments, but gcc or clang doesn't complain that the mock will essentially have no arguments passed to it, and works perfectly as a stand in. it's this line here type name() ...
Here is the usage. Notice I am mocking add.c capability in this test file.
#include "foo.h"
#include "testframework.h"
DECLARE_MOCK(int, add);
int main() {
DESCRIBE("things");
MOCK(add, 2);
SHOULDB("add", {
ASSERT(add(0, 2) == 2);
});
}
The issue comes in on gcc mac, which complains.
[INFO] CMD: gcc -Wall -Werror -std=c11 -O3 -o target/things tests/things.c obj/things/lib.o
tests/things.c:29:57: error: too many arguments in call to 'add' [-Werror]
SHOULDB("add", { ASSERT(add(0, 2) == 2); });
I would like to keep -Wall and -Werror, I was hoping there was an attribute I could add to the macro, which is the opposite of sentinel.

Is there equivelant "-fno-common" compile option for Visual Sutdio for pure C?

I'm new to strong & weak symbol concepts. For the following example (pure C) code, x is defined twice, one strong and one weak. I'd like to make my compiler report error:
foo.c:
#include <stdio.h>
void f(void);
int x = 15213;
int main(){
f();
printf("x = %d\n", x);
return 0;
}
bar.c
int x;
void f(){
x = 15212;
}
For gcc, "-fno-common" is what I want:
gcc -o foobar foo.c bar.c -fno-common
Thus it reports redefined symbols (x).
Is there an equivalent compile option in Visual Studio? (Correct me if I'm wrong described)
This is a link option: not a compile option. The compiler doesn't know anything about x being declared anywhere else except in the file it is compiling. The linker, however, sees two x declarations and will generate error 2005 or 1169.

Linking C++ static libraries with QtCreator

I am using QtCreator 4.2.1 with MinGW4.9.2 32 bit compiler (Win 8.1 platform).
My pgi project only has a main.cpp which uses objects and functions belonging to my PhygenicLib static library. This static library in turn relies on functions and classes and constants defined in the Mathlib2 static library along with the eigen3 template library (template-based, only headers, no .cpp!) found on the web.
Here is the PhygenicLib.pro which compiles nicely into the libPhygenicLib.a file (debug mode):
QT -= gui
TARGET = PhygenicLib
TEMPLATE = lib
CONFIG += staticlib
DEFINES += QT_DEPRECATED_WARNINGS
INCLUDEPATH += d:/Qt-apps/Mathlib2 d:/Qt-apps/eigen3
SOURCES += phygenic.cpp \ cinemeca_pg.cpp \ mecagen.cpp \
rc_perf_pg.cpp \ xploit_pg.cpp \ utilities.cpp \
Analex.cpp \ Anasyntax.cpp
HEADERS += phygenic.h \ cinemeca_pg.h \ mecagen.h \ rc_perf_pg.h \
xploit_pg.h \ utilities.h \ Analex.h \ Anasyntax.h
unix { target.path = /usr/lib
INSTALLS += target }
Here is the pgi.pro (debug mode):
QT += core
QT -= gui
CONFIG += c++11
TARGET = pgi
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
LIBS += d:/Qt-apps/build-Mathlib2-Desktop_Qt_5_5_1_MinGW_32bit-Release/release/libMathlib2.a \
d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a
INCLUDEPATH += d:/Qt-apps/PhygenicLib \
d:/Qt-apps/Mathlib2 \
d:/Qt-apps/eigen3
SOURCES += main.cpp
HEADERS +=
DEFINES += QT_DEPRECATED_WARNINGS
Note that Mathlib2 is compiled in release mode not debug mode, but that should not be a problem (?). Now, here is my main.cpp:
#include <iostream>
#include "Anasyntax.h"
using namespace std;
int main(int, char**)
{
ofstream fdmp("test"+extFichier[DMP]);
try { // bloc try
fdmp<<"--- test généraux interpréteur ---"<<endl ;
Anasyntax asynt ;
asynt.interprete(fdmp);
} catch (std::exception const& err) {
fdmp << err.what()<< endl<<" - Erreur fatale." << endl ;
cerr << err.what()<< endl<<"- Erreur fatale." << endl ;
}
cout<<'\a' ;
fdmp << "--- FIN DE L'EXECUTION --- "<< std::endl;
fdmp.close() ;
return EXIT_SUCCESS ;
}
#include Anasyntax.h is pulling all the headers from the various libraries wherever required (root of the include tree, so to say).
Now when I try to compile and link the project, I get the following diagnostic (sorry the language, QtCreator installed in French):
14:48:12: Exécution des étapes pour le projet pgi...
14:48:12: Débute : "C:\Qt\Tools\mingw492_32\bin\mingw32-make.exe" clean
C:/Qt/Tools/mingw492_32/bin/mingw32-make -f Makefile.Debug clean
mingw32-make[1]: Entering directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
del debug\main.o
Impossible de trouver D:\Qt-apps\build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug\debug\main.o
mingw32-make[1]: Leaving directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
C:/Qt/Tools/mingw492_32/bin/mingw32-make -f Makefile.Release clean
mingw32-make[1]: Entering directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
del release\main.o
Impossible de trouver D:\Qt-apps\build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug\release\main.o
mingw32-make[1]: Leaving directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
14:48:13: Le processus "C:\Qt\Tools\mingw492_32\bin\mingw32-make.exe" s'est terminé normalement.
14:48:13: Configuration inchangée, étape qmake sautée.
14:48:13: Débute : "C:\Qt\Tools\mingw492_32\bin\mingw32-make.exe"
C:/Qt/Tools/mingw492_32/bin/mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
g++ -c -pipe -fno-keep-inline-dllexport -g -std=c++0x -frtti -Wall -Wextra -fexceptions -mthreads -DUNICODE -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_CORE_LIB -I..\pgi -I. -Id:\Qt-apps\PhygenicLib -Id:\Qt-apps\Mathlib2 -Id:\Qt-apps\eigen3 -IC:\Qt\5.5\mingw492_32\include -IC:\Qt\5.5\mingw492_32\include\QtCore -Idebug -IC:\Qt\5.5\mingw492_32\mkspecs\win32-g++ -o debug\main.o ..\pgi\main.cpp
g++ -Wl,-subsystem,console -mthreads -o debug\pgi.exe debug/main.o d:/Qt-apps/build-Mathlib2-Desktop_Qt_5_5_1_MinGW_32bit-Release/release/libMathlib2.a d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a -LC:/Qt/5.5/mingw492_32/lib -lQt5Cored
d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a(mecagen.o): In function `ZN10Solide_dyn4calcEv':
D:\Qt-apps\build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/../PhygenicLib/mecagen.cpp:220: undefined reference to `rotation_WM(V3)'
D:\Qt-apps\build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/../PhygenicLib/mecagen.cpp:221: undefined reference to `Mat_transp_3x3(Mat3x3)'
and so on, and so on...
It looks like g++ is trying to link mecagen.cpp (which is already compiled as part of libPhygenicLib.a) and not finding references to symbols which are part of Mathlib2 library (includepath= d:/Qt-apps/Mathlib2, LIB= d:/.../release/libMathlib2.a).
I found a walk-around solution: just inserting in the main.cpp a reference to any Mathlib2 object or function, such as function coupure hereafter, enables to build and run the project executable without any warning/error:
int main(int, char**)
{
ofstream fdmp("test"+extFichier[DMP]);
coupure(0.,0.,0.) ; // any object or function from Mathlib2 works as well!
try { // etc...
So the problem is overcome but it drives me nuts not to understand why it won't work without inserting a useless code line... I have to say I was too lazy to ever learn about g++, make, qmake and makeFile sort of things; just pushing QtCreator buttons is so easy and comfortable...
I read again some posts with somewhat similar issues and found one comment regarding the order of declaring static libraries which may be critical in some cases.
And indeed, if I declared the LIBS in my pgi.pro in the reverse order, as follows:
LIBS += d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a \
d:/Qt-apps/build-Mathlib2-Desktop_Qt_5_5_1_MinGW_32bit-Release/release/libMathlib2.a
the project then compiles and links fine without any extraneous reference to a Mathlib2 object or function in the main.cpp. I got the lesson and will now take care of declaring LIBS in the appropriate order...

Weird pointer conversion in C

I'm having trouble while writing my garbage collector in C. I give you a minimal and verifiable example for it.
The first file is in charge of dealing with the virtual machine
#include <stdlib.h>
#include <stdint.h>
typedef int32_t value_t;
typedef enum {
Lb, Lb1, Lb2, Lb3, Lb4, Lb5,
Ib, Ob
} reg_bank_t;
static value_t* memory_start;
static value_t* R[8];
value_t* engine_get_Lb(void) { return R[Lb]; }
value_t engine_run() {
memory_start = memory_get_start();
for (reg_bank_t pseudo_bank = Lb; pseudo_bank <= Lb5; ++pseudo_bank)
R[pseudo_bank] = memory_start + (pseudo_bank - Lb) * 32;
value_t* block = memory_allocate();
}
Then I have the actual garbage collector, the minimized code is:
#include <stdlib.h>
#include <stdint.h>
typedef int32_t value_t;
static value_t* memory_start = NULL;
void memory_setup(size_t total_byte_size) {
memory_start = calloc(total_byte_size, 1);
}
void* memory_get_start() { return memory_start; }
void mark(value_t* base){
value_t vbase = 0;
}
value_t* memory_allocate() {
mark(engine_get_Lb());
return engine_get_Lb();
}
Finally, minimal main is:
int main(int argc, char* argv[]) {
memory_setup(1000000);
engine_run();
return 0;
}
The problem I'm getting with gdb is that if I print engine_get_Lb() I get the address (value_t *) 0x7ffff490a800 while when printing base inside of the function mark I get the address (value_t *) 0xfffffffff490a800.
Any idea why this is happening?
Complementary files that may help
The makefile
SHELL=/bin/bash
SRCS=src/engine.c \
src/main.c \
src/memory_mark_n_sweep.c
CFLAGS_COMMON=-std=c11 -fwrapv
CLANG_SAN_FLAGS=-fsanitize=address
# Clang warning flags
CLANG_WARNING_FLAGS=-Weverything \
-Wno-format-nonliteral \
-Wno-c++98-compat \
-Wno-gnu-label-as-value
# Flags for debugging:
CFLAGS_DEBUG=${CFLAGS_COMMON} -g ${CLANG_SAN_FLAGS} ${CLANG_WARNING_FLAGS}
# Flags for maximum performance:
CFLAGS_RELEASE=${CFLAGS_COMMON} -O3 -DNDEBUG
CFLAGS=${CFLAGS_DEBUG}
all: vm
vm: ${SRCS}
mkdir -p bin
clang ${CFLAGS} ${LDFLAGS} ${SRCS} -o bin/vm
File with instructions .asm
5c190000 RALO(Lb,25)
value_t* memory_allocate() {
mark(engine_get_Lb());
return engine_get_Lb();
}
engine_get_Lb is not declared before use. It is assumed by the compiler to return int, per an antiquated and dangerous rule of the C language. It was deprecated in the C standard for quite some time, and now is finally removed.
Create a header file with declarations of all your global functions, and #include it in all your source files.
Your compiler should have at least warned you about this error at its default settings. If it did, you should have read and completely understood the warnings before continuing. If it didn't, consider an upgrade. If you cannot upgrade, permanently add -Wall -Wextra -Werror to your compilation flags. Consider also -Wpedantic and -std=c11.

undefined reference to `main' in C

Hi I am getting below error while compiling a c code using gcc
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: ld returned 1 exit status
I am trying to import the fftw() function into SystemVerilog. Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <fftw3.h>
void fftw(double FFT_in[],int size)
{
double *IFFT_out;
int i;
fftw_complex *middle;
fftw_plan fft;
fftw_plan ifft;
middle = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*size);
IFFT_out = (double *) malloc(size*sizeof(double));
fft = fftw_plan_dft_r2c_1d(size, FFT_in, middle, FFTW_ESTIMATE); //Setup fftw plan for fft (real 1D data)
ifft = fftw_plan_dft_c2r_1d(size, middle, IFFT_out, FFTW_ESTIMATE); //Setup fftw plan for ifft
fftw_execute(fft);
fftw_execute(ifft);
printf("Input: \tFFT_coefficient[i][0] \tFFT_coefficient[i][1] \tRecovered Output:\n");
for(i=0;i<size;i++)
printf("%f\t%f\t\t\t%f\t\t\t%f\n",FFT_in[i],middle[i][0],middle[i][1],IFFT_out[i]/size);
fftw_destroy_plan(fft);
fftw_destroy_plan(ifft);
fftw_free(middle);
free(IFFT_out);
//return IFFT_out;
}
Here is a system Verilog code from where I am trying to call fftw
module top;
import "DPI-C" function void fftw(real FFT_in[0:11], int size);
real j [0:11];
integer i,size;
real FFT_in [0:11];
initial begin
size = 12;
FFT_in[0] = 0.1;
FFT_in[1] = 0.6;
FFT_in[2] = 0.1;
FFT_in[3] = 0.4;
FFT_in[4] = 0.5;
FFT_in[5] = 0.0;
FFT_in[6] = 0.8;
FFT_in[7] = 0.7;
FFT_in[8] = 0.8;
FFT_in[9] = 0.6;
FFT_in[10] = 0.1;
FFT_in[11] = 0.0;
$display("Entering in SystemVerilog Initial Block\n");
#20
fftw(FFT_in,size);
$display("Printing recovered output from system verilog\n");
//for(i=0;i<size;i++)
//$display("%f\t\n",(j[i])/size);
$display("Exiting from SystemVerilog Initial Block");
#5 $finish;
end
endmodule
Here is an irun command to compile both systemverilg and C files
# Compile the SystemVerilog files
fftw_test.sv
-access +rwc
# Generate a header file called _sv_export.h
-dpiheader _sv_export.h
# Delay compilation of fftw_test.c until after elaboration
#-cpost fftw_test_DPI.c -end
-I/home/fftw/local/include -L/home/ss69/fftw/local/lib fftw_test_DPI.c -lfftw3 -lm
# Redirect output of ncsc_run to a log file called ncsc_run.log
-log_ncsc_run ncsc_run.log
while running this command give below error:
building library run.so
ld: /home/fftw/local/lib/libfftw3.a(mapflags.o): relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/homefftw/local/lib/libfftw3.a: could not read symbols: Bad value
collect2: ld returned 1 exit status
make: * [/home/ss69/DPI/./INCA_libs/irun.lnx8664.12.20.nc/librun.so] Error 1
ncsc_run: *E,TBBLDF: Failed to build test library
/home/DPI/./INCA_libs/irun.lnx8664.12.20.nc/librun.so
irun: *E,CCERR: Error during cc compilation (status 1), exiting.
When I simply try to compile C using gcc with below command:
gcc -g -Wall -Werror -I/home/fftw/local/include -L/home/ss69/fftw/local/lib \
fftw_test_DPI.c -lfftw3 -lm -o fftw_test_DPI
I get this error:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../lib64/crt1.o: In function _start':
(.text+0x20): undefined reference tomain'
collect2: ld returned 1 exit status
Exactly how are you using the function void fftw(double FFT_in[],int size) from your comments it sounds like you are coding routine that is called as DLL or as part of a static library.
If this is the case then adding main() isn't going to help, at all.
What you have written is ABSOLUTELY 100% OK, if it is to be used as a callable routine.
What you might need to do is compile this routine into a library, even a static lib. is probably ok. If this is the case then consult your GCC documentation on how to create a static or dynamic lib.
Finally, I have written Verilog code myself, so you can also provide any lines or references to Verilog documentation that you have read and whose instructions you are following. I assume that at some point you are invoking Verilog and supplying it with a list of libraries it can/should use. Your lib should be included in that list.
Am including comments from jxh per his request:
To import the function into SystemVerilog, you need to compile your function into a shared object. Then, you would point SystemVerilog at the shared object. (I don't use SystemVerilog, but that is what I gather from its web page.)
gcc -shared -fPIC -g -Wall -Werror \
-I/home/ss69/fftw/local/include -L/home/ss69/fftw/local/lib \
fftw_test_DPI.c -lfftw3 -lm -o libfftw_test_DPI.so
Your are missing #include "svdpi.h" in the fftwc.c file (or maybe you are not showing it because it is in fftwc.h). This include is needed for DPI.
You are compiling a DPI library to be used with a SystemVerilog simulator. Therefore, you do not need a main() method.
I prefer to always compile all DPI methods outside of the SystemVerilog compiler. The include the DPI library to the simulation phase. My flow looks something like the following:
${SVTOOL} -compile -f svfiles.f -dpi_header gen_dpi_header.h
gcc -fPIC -pipe -O2 -c -g \
-I${SVTOOL_PATH}/include -Imy_dpi_dir -I. \
-o fftw_test_DPI.o \
fftw_test_DPI.c
gcc -shared -o libdpi.so \
fftw_test_DPI.o [other object files]
# then call ${SVTOOL} again for simulation with libdpi.so
If you cannot get past the first gcc stage then your issue is clearly on the C side.
I do not have access to the fftw3 library at the moment. I'm wondering your void fftw(double FFT_in[],int size) might be clobbering a library function. Try renaming it void dpi_fftw(double FFT_in[],int size)
You have no main function. Every binary must define main. If it doesn't, you don't have a null region of memory that _start defines in the binary, which means your program can't start!
Add a function:
int main(){
fftw(doubleArgumentsArray, intArgument); //Or whatever function calls this function
return 1; //Needed for C89, C99 will automatically return 1
}
Have found the following tutorial on Dynamic Programming Interface (DPI) :
http://www.doulos.com/knowhow/sysverilog/tutorial/dpi/
Specifically, scroll down to the "Including Foreign Language Code".
It should help with background information about how to construct a C modules for SystemVerilog.
Also, the tutorial has the following import statement:
import "DPI" function void slave_write(input int address, input int data);
This SystemVerilog statement obviously has input defs on the parameters, is this required? Your import does NOT identify input vs. output??
I believe this is an issue with some gcc linkers. I added the following linker flag:
irun ... -Wld,-B/usr/lib/x86_64-linux-gnu
And it fixed the issue.

Resources