Skip to content

Commit

Permalink
Fix #5459
Browse files Browse the repository at this point in the history
This combines the .o emission code from #5787 with some error checking code
for cpuid mismatch. When releasing a binary, set JULIA_CPU_TARGET to "core2"
(we discussed that this is a reasonable minimum) in your Make.user and
everything should work just fine.
  • Loading branch information
Keno committed Mar 19, 2014
1 parent 9a5c276 commit c90c0af
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 25 deletions.
27 changes: 20 additions & 7 deletions Make.inc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ USE_INTEL_JITEVENTS = 0
# libc++ is standard on OS X 10.9, but not for earlier releases
USE_LIBCPP = 0

# Select the cpu architecture to target
# Current available options are "native" and "core2"
JULIA_CPU_TARGET = native

# we include twice to pickup user definitions better
ifeq (exists, $(shell [ -e $(JULIAHOME)/Make.user ] && echo exists ))
include $(JULIAHOME)/Make.user
Expand Down Expand Up @@ -207,7 +211,8 @@ $(error Address Sanitizer only supported with clang. Try setting SANITIZE=0)
endif
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
JCFLAGS = -std=gnu99 -pipe $(fPIC) -fno-strict-aliasing -D_FILE_OFFSET_BITS=64
JCFLAGS = -std=gnu99 -pipe $(fPIC) -fno-strict-aliasing
JCPPFLAGS = -D_FILE_OFFSET_BITS=64
JCXXFLAGS = -pipe $(fPIC) -fno-rtti
DEBUGFLAGS = -O0 -ggdb3 -DDEBUG -fstack-protector-all
SHIPFLAGS = -O3 -falign-functions
Expand All @@ -216,7 +221,8 @@ endif
ifeq ($(USECLANG),1)
CC = $(CROSS_COMPILE)clang
CXX = $(CROSS_COMPILE)clang++
JCFLAGS = -pipe $(fPIC) -fno-strict-aliasing -D_FILE_OFFSET_BITS=64
JCFLAGS = -pipe $(fPIC) -fno-strict-aliasing
JCPPFLAGS = -D_FILE_OFFSET_BITS=64
JCXXFLAGS = -pipe $(fPIC) -fno-rtti
DEBUGFLAGS = -O0 -g -DDEBUG -fstack-protector-all
SHIPFLAGS = -O3
Expand All @@ -229,7 +235,7 @@ else
CC += $(STDLIBCPP_FLAG) -mmacosx-version-min=10.6
CXX += $(STDLIBCPP_FLAG) -mmacosx-version-min=10.6
endif
JCFLAGS += -D_LARGEFILE_SOURCE -D_DARWIN_USE_64_BIT_INODE=1
JCPPFLAGS += -D_LARGEFILE_SOURCE -D_DARWIN_USE_64_BIT_INODE=1
endif
endif

Expand All @@ -241,6 +247,14 @@ AS := $(CROSS_COMPILE)as
LD := $(CROSS_COMPILE)ld
RANLIB := $(CROSS_COMPILE)ranlib

ifeq ($(JULIA_CPU_TARGET),native)
JCPPFLAGS += -DJULIA_TARGET_NATIVE
else ifeq ($(JULIA_CPU_TARGET),core2)
JCPPFLAGS += -DJULIA_TARGET_CORE2
else
$(error Unknown cpu target architecture)
endif


# Calculate relative paths to libdir and private_libdir
build_libdir_rel = $(shell $(JULIAHOME)/contrib/relative_path.sh $(build_bindir) $(build_libdir))
Expand All @@ -250,7 +264,7 @@ build_private_libdir_rel = $(shell $(JULIAHOME)/contrib/relative_path.sh $(build
private_libdir_rel = $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(private_libdir))

# if not absolute, then relative to the directory of the julia executable
JCFLAGS += "-DJL_SYSTEM_IMAGE_PATH=\"$(build_private_libdir_rel)/sys.ji\""
JCPPFLAGS += "-DJL_SYSTEM_IMAGE_PATH=\"$(build_private_libdir_rel)/sys.ji\""

# On Windows, we want shared library files to end up in $(build_bindir), instead of $(build_libdir)
ifeq ($(OS),WINNT)
Expand Down Expand Up @@ -321,7 +335,7 @@ endif
else
ifeq ($(OS),Darwin)
LIBUNWIND=$(build_libdir)/libosxunwind.a
JCFLAGS+=-DLIBOSXUNWIND
JCPPFLAGS+=-DLIBOSXUNWIND
else
LIBUNWIND=$(build_libdir)/libunwind-generic.a $(build_libdir)/libunwind.a
endif
Expand Down Expand Up @@ -485,8 +499,7 @@ endif
ifeq ($(OS), WINNT)
OSLIBS += -Wl,--export-all-symbols -Wl,--version-script=$(JULIAHOME)/src/julia.expmap \
$(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -lssp
JCFLAGS += -D_WIN32_WINNT=0x0600
JCXXFLAGS += -D_WIN32_WINNT=0x0600
JCPPFLAGS += -D_WIN32_WINNT=0x0600
JLDFLAGS = -Wl,--stack,8388608
ifeq ($(ARCH),i686)
JLDFLAGS += -Wl,--large-address-aware
Expand Down
9 changes: 3 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ $(build_sysconfdir)/julia/juliarc.jl: contrib/windows/juliarc.jl
endif

# use sys.ji if it exists, otherwise run two stages
$(build_private_libdir)/sys%ji: $(build_private_libdir)/sys%bc

$(build_private_libdir)/sys%o: $(build_private_libdir)/sys%bc
$(call spawn,$(LLVM_LLC)) -filetype=obj -relocation-model=pic -mattr=-bmi2,-avx2 -o $(call cygpath_w,$@) $(call cygpath_w,$<)
$(build_private_libdir)/sys%ji: $(build_private_libdir)/sys%o

.PRECIOUS: $(build_private_libdir)/sys%o

Expand All @@ -77,11 +74,11 @@ $(build_private_libdir)/sys%$(SHLIB_EXT): $(build_private_libdir)/sys%o
$$([ $(OS) = Darwin ] && echo -Wl,-undefined,dynamic_lookup || echo -Wl,--unresolved-symbols,ignore-all ) \
$$([ $(OS) = WINNT ] && echo -ljulia -lssp)

$(build_private_libdir)/sys0.bc:
$(build_private_libdir)/sys0.o:
@$(QUIET_JULIA) cd base && \
$(call spawn,$(JULIA_EXECUTABLE)) --build $(call cygpath_w,$(build_private_libdir)/sys0) sysimg.jl

$(build_private_libdir)/sys.bc: VERSION base/*.jl base/pkg/*.jl base/linalg/*.jl base/sparse/*.jl $(build_datarootdir)/julia/helpdb.jl $(build_datarootdir)/man/man1/julia.1 $(build_private_libdir)/sys0.$(SHLIB_EXT)
$(build_private_libdir)/sys.o: VERSION base/*.jl base/pkg/*.jl base/linalg/*.jl base/sparse/*.jl $(build_datarootdir)/julia/helpdb.jl $(build_datarootdir)/man/man1/julia.1 $(build_private_libdir)/sys0.$(SHLIB_EXT)
@$(QUIET_JULIA) cd base && \
$(call spawn,$(JULIA_EXECUTABLE)) --build $(call cygpath_w,$(build_private_libdir)/sys) \
-J$(call cygpath_w,$(build_private_libdir))/$$([ -e $(build_private_libdir)/sys.ji ] && echo sys.ji || echo sys0.ji) -f sysimg.jl \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ include $(JULIAHOME)/deps/Versions.make

override CFLAGS += $(JCFLAGS)
override CXXFLAGS += $(JCXXFLAGS)
override CPPFLAGS += $(JCPPFLAGS)

SRCS = \
jltypes gf ast builtins module codegen interpreter \
Expand Down
28 changes: 28 additions & 0 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ extern "C" int32_t jl_get_llvm_gv(jl_value_t *p)
return 0;
return it->second.index;
}

extern "C" {
extern void cpuid(int32_t CPUInfo[4], int32_t InfoType);
}

static void jl_gen_llvm_gv_array()
{
// emit the variable table into the code image (can only call this once)
Expand All @@ -234,6 +239,29 @@ static void jl_gen_llvm_gv_array()
GlobalVariable::ExternalLinkage,
ConstantInt::get(T_size,globalUnique+1),
"jl_globalUnique");

Constant *feature_string = ConstantDataArray::getString(jl_LLVMContext,jl_cpu_string);
new GlobalVariable(
*jl_Module,
feature_string->getType(),
true,
GlobalVariable::ExternalLinkage,
feature_string,
"jl_sysimg_cpu_target");

// For native also store the cpuid
if(strcmp(jl_cpu_string,"native") == 0) {
uint32_t info[4];

cpuid((int32_t*)info, 1);
new GlobalVariable(
*jl_Module,
T_int64,
true,
GlobalVariable::ExternalLinkage,
ConstantInt::get(T_int64,((uint64_t)info[2])|(((uint64_t)info[3])<<32)),
"jl_sysimg_cpu_cpuid");
}
}

static int32_t jl_assign_functionID(Function *functionObject)
Expand Down
72 changes: 66 additions & 6 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/ExecutionEngine/JITMemoryManager.h"
#include "llvm/PassManager.h"
#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Bitcode/ReaderWriter.h"
#if defined(LLVM_VERSION_MAJOR) && LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 5
#define LLVM35 1
#include "llvm/IR/Verifier.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/AsmParser/Parser.h"
#else
Expand Down Expand Up @@ -80,6 +83,7 @@
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Instrumentation.h"
#if defined(LLVM_VERSION_MAJOR) && LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 1
#include "llvm/Transforms/Vectorize.h"
#endif
Expand Down Expand Up @@ -127,6 +131,7 @@ static LLVMContext &jl_LLVMContext = getGlobalContext();
static IRBuilder<> builder(getGlobalContext());
static bool nested_compile=false;
static ExecutionEngine *jl_ExecutionEngine;
static TargetMachine *jl_TargetMachine;
#ifdef USE_MCJIT
static Module *shadow_module;
static RTDyldMemoryManager *jl_mcjmm;
Expand Down Expand Up @@ -302,6 +307,47 @@ void jl_dump_bitcode(char* fname)
#endif
}

extern "C"
void jl_dump_objfile(char* fname, int jit_model)
{
std::string err;
raw_fd_ostream OS(fname, err);
formatted_raw_ostream FOS(OS);
jl_gen_llvm_gv_array();

// We don't want to use MCJIT's target machine because
// it uses the large code model and we may potentially
// want less optimizations there.
OwningPtr<TargetMachine>
TM(jl_TargetMachine->getTarget().createTargetMachine(
jl_TargetMachine->getTargetTriple(),
jl_TargetMachine->getTargetCPU(),
jl_TargetMachine->getTargetFeatureString(),
jl_TargetMachine->Options,
#ifdef _OS_LINUX_
Reloc::PIC_,
#else
jit_model ? Reloc::PIC_ : Reloc::Default,
#endif
jit_model ? CodeModel::JITDefault : CodeModel::Default,
CodeGenOpt::Aggressive // -O3
));

PassManager PM;
PM.add(new TargetLibraryInfo(Triple(jl_TargetMachine->getTargetTriple())));
PM.add(new DataLayout(*jl_ExecutionEngine->getDataLayout()));
if (TM->addPassesToEmitFile(PM, FOS,
TargetMachine::CGFT_ObjectFile, false)) {
jl_error("Could not generate obj file for this target");
}

#ifdef USE_MCJIT
PM.run(*shadow_module);
#else
PM.run(*jl_Module);
#endif
}

// aggregate of array metadata
typedef struct {
Value *dataptr;
Expand Down Expand Up @@ -366,13 +412,20 @@ static Type *NoopType;

// --- utilities ---

#if defined(JULIA_TARGET_CORE2)
const char *jl_cpu_string = "core2";
#elif defined(JULIA_TARGET_NATIVE)
const char *jl_cpu_string = "native";
#else
#error "Must select julia cpu target"
#endif

extern "C" {
int globalUnique = 0;
}

#include "cgutils.cpp"


static void jl_rethrow_with_add(const char *fmt, ...)
{
if (jl_typeis(jl_exception_in_transit, jl_errorexception_type)) {
Expand Down Expand Up @@ -4082,23 +4135,30 @@ extern "C" void jl_init_codegen(void)
#endif
#ifdef USE_MCJIT
jl_mcjmm = new SectionMemoryManager();
#endif
#else
// Temporarily disable Haswell BMI2 features due to LLVM bug.
const char *mattr[] = {"-bmi2", "-avx2"};
#endif
std::vector<std::string> attrvec (mattr, mattr+2);
jl_ExecutionEngine = EngineBuilder(engine_module)
EngineBuilder eb = EngineBuilder(engine_module)
.setEngineKind(EngineKind::JIT)
#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_)
.setJITMemoryManager(new JITMemoryManagerWin())
#endif
.setTargetOptions(options)
#if defined(JULIA_TARGET_NATIVE)
.setMCPU("")
#else
.setMCPU(jl_cpu_string)
#endif
#ifdef USE_MCJIT
.setUseMCJIT(true)
.setMAttrs(attrvec)
.setMAttrs(attrvec);
#else
.setMAttrs(attrvec)
.setMAttrs(attrvec);
#endif
.create();
jl_TargetMachine = eb.selectTarget();
jl_ExecutionEngine = eb.create(jl_TargetMachine);
#endif // LLVM VERSION
jl_ExecutionEngine->DisableLazyCompilation();

Expand Down
22 changes: 21 additions & 1 deletion src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ static void write_as_tag(ios_t *s, uint8_t tag)
static void jl_serialize_value_(ios_t *s, jl_value_t *v);
static jl_value_t *jl_deserialize_value(ios_t *s);
static jl_value_t *jl_deserialize_value_internal(ios_t *s);
static jl_value_t ***sysimg_gvars = NULL;
jl_value_t ***sysimg_gvars = NULL;

extern int globalUnique;
extern void cpuid(int32_t CPUInfo[4], int32_t InfoType);
extern const char *jl_cpu_string;

static void jl_load_sysimg_so(char *fname)
{
Expand All @@ -108,6 +110,24 @@ static void jl_load_sysimg_so(char *fname)
if (sysimg_handle != 0) {
sysimg_gvars = (jl_value_t***)jl_dlsym(sysimg_handle, "jl_sysimg_gvars");
globalUnique = *(size_t*)jl_dlsym(sysimg_handle, "jl_globalUnique");
const char *cpu_target = (const char*)jl_dlsym(sysimg_handle, "jl_sysimg_cpu_target");
if (strcmp(cpu_target,jl_cpu_string) != 0)
jl_error("Julia and the system image were compiled for different architectures.");

This comment has been minimized.

Copy link
@staticfloat

staticfloat Jun 6, 2014

Member

What is the worst that can happen here? In #7124, I'm experimenting with making jl_cpu_string a runtime option so that we can do things like ship an i386 sys.so, and then on startup Julia can check CPU capabilities and notify users that they can rebuild their system image for faster startup.

I started getting warnings from here, but since I'm going from restricted image -> unrestricted executable, I'm pretty sure nothing bad is going to happen. The bad stuff happens when you have a restricted executable running an unrestricted image, am I right? I think it might be worthwhile to have a more complex test here where we can compare individual CPU features and still load a system image that has a subset of the currently available CPU features, but not a superset. Does that sound reasonable?

This comment has been minimized.

Copy link
@Keno

Keno Jun 6, 2014

Author Member

False. LLVM switches calling conventions on you depending on what is available. The precompiled code may pass it in one register and LLVM may expect it in another.

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Jun 6, 2014

Member

In other words, it's not just a matter of subsets of features --- if only. Code for different architectures can be incompatible in other ways.

uint32_t info[4];
cpuid((int32_t*)info, 1);
if (strcmp(cpu_target,"native") == 0)
{
uint64_t saved_cpuid = *(uint64_t*)jl_dlsym(sysimg_handle, "jl_sysimg_cpu_cpuid");
if (saved_cpuid != (((uint64_t)info[2])|(((uint64_t)info[3])<<32)))
jl_error("Target architecture mismatch. Please regenerate sys.{so,dll,dylib}.");
} else if(strcmp(cpu_target,"core2") == 0) {
int HasSSSE3 = (info[3] & 1<<9);
if (!HasSSSE3)
jl_error("The current host does not support SSSE3, but the system image was compiled for Core2.\n"
"Please delete or regenerate sys.{so,dll,dylib}.");
} else {
jl_error("System image has unknown target cpu architecture.");
}
}
else {
sysimg_gvars = 0;
Expand Down
10 changes: 5 additions & 5 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,13 +867,13 @@ DLLEXPORT int julia_trampoline(int argc, char **argv, int (*pmain)(int ac,char *
if (asprintf(&build_ji, "%s.ji",build_path) > 0) {
jl_save_system_image(build_ji);
free(build_ji);
char *build_bc;
if (asprintf(&build_bc, "%s.bc",build_path) > 0) {
jl_dump_bitcode(build_bc);
free(build_bc);
char *build_o;
if (asprintf(&build_o, "%s.o",build_path) > 0) {
jl_dump_objfile(build_o,0);
free(build_o);
}
else {
ios_printf(ios_stderr,"FATAL: failed to create string for .bc build path");
ios_printf(ios_stderr,"FATAL: failed to create string for .o build path");
}
}
else {
Expand Down
1 change: 1 addition & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ void jl_init_tasks(void *stack, size_t ssize);
void jl_init_serializer(void);

void jl_dump_bitcode(char *fname);
void jl_dump_objfile(char *fname, int jit_model);
int32_t jl_get_llvm_gv(jl_value_t *p);

#ifdef _OS_LINUX_
Expand Down
1 change: 1 addition & 0 deletions ui/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ include $(JULIAHOME)/Make.inc

override CFLAGS += $(JCFLAGS)
override CXXFLAGS += $(JCXXFLAGS)
override CPPFLAGS += $(JCPPFLAGS)

FLAGS = -Wall -Wno-strict-aliasing -fno-omit-frame-pointer \
-I$(JULIAHOME)/src -I$(JULIAHOME)/src/support -I$(build_includedir)
Expand Down

0 comments on commit c90c0af

Please sign in to comment.