Mercurial > projects > ldc
comparison gen/toobj.cpp @ 676:1f0a78174598
Make ldc call gcc to assemble.
author | Christian Kamm <kamm incasoftware de> |
---|---|
date | Sat, 11 Oct 2008 20:00:36 +0200 |
parents | bfe5229f9d8e |
children | 075c1272a01d |
comparison
equal
deleted
inserted
replaced
675:bfe5229f9d8e | 676:1f0a78174598 |
---|---|
18 #include "llvm/Target/TargetMachine.h" | 18 #include "llvm/Target/TargetMachine.h" |
19 #include "llvm/Target/TargetMachineRegistry.h" | 19 #include "llvm/Target/TargetMachineRegistry.h" |
20 #include "llvm/Module.h" | 20 #include "llvm/Module.h" |
21 #include "llvm/ModuleProvider.h" | 21 #include "llvm/ModuleProvider.h" |
22 #include "llvm/PassManager.h" | 22 #include "llvm/PassManager.h" |
23 #include "llvm/System/Program.h" | |
23 #include "llvm/System/Path.h" | 24 #include "llvm/System/Path.h" |
24 #include "llvm/Support/raw_ostream.h" | 25 #include "llvm/Support/raw_ostream.h" |
25 | 26 |
26 #include "mars.h" | 27 #include "mars.h" |
27 #include "module.h" | 28 #include "module.h" |
56 // in gen/optimize.cpp | 57 // in gen/optimize.cpp |
57 void ldc_optimize_module(llvm::Module* m, char lvl, bool doinline); | 58 void ldc_optimize_module(llvm::Module* m, char lvl, bool doinline); |
58 | 59 |
59 // fwd decl | 60 // fwd decl |
60 void write_asm_to_file(llvm::Module& m, llvm::raw_fd_ostream& Out); | 61 void write_asm_to_file(llvm::Module& m, llvm::raw_fd_ostream& Out); |
62 void assemble(const llvm::sys::Path& asmpath, const llvm::sys::Path& objpath, char** envp); | |
61 | 63 |
62 ////////////////////////////////////////////////////////////////////////////////////////// | 64 ////////////////////////////////////////////////////////////////////////////////////////// |
63 | 65 |
64 void Module::genobjfile(int multiobj) | 66 void Module::genobjfile(int multiobj, char** envp) |
65 { | 67 { |
66 bool logenabled = Logger::enabled(); | 68 bool logenabled = Logger::enabled(); |
67 if (llvmForceLogging && !logenabled) | 69 if (llvmForceLogging && !logenabled) |
68 { | 70 { |
69 Logger::enable(); | 71 Logger::enable(); |
207 if (!global.params.output_s) { | 209 if (!global.params.output_s) { |
208 spath.createTemporaryFileOnDisk(); | 210 spath.createTemporaryFileOnDisk(); |
209 } | 211 } |
210 Logger::println("Writing native asm to: %s\n", spath.c_str()); | 212 Logger::println("Writing native asm to: %s\n", spath.c_str()); |
211 std::string err; | 213 std::string err; |
212 llvm::raw_fd_ostream out(spath.c_str(), err); | 214 { |
213 write_asm_to_file(*ir.module, out); | 215 llvm::raw_fd_ostream out(spath.c_str(), err); |
214 | 216 write_asm_to_file(*ir.module, out); |
215 //TODO: call gcc to convert assembly to object file | 217 } |
218 | |
219 // call gcc to convert assembly to object file | |
220 LLPath objpath = LLPath(objfile->name->toChars()); | |
221 assemble(spath, objpath, envp); | |
216 | 222 |
217 if (!global.params.output_s) { | 223 if (!global.params.output_s) { |
218 spath.eraseFromDisk(); | 224 spath.eraseFromDisk(); |
219 } | 225 } |
220 | 226 |
228 } | 234 } |
229 } | 235 } |
230 | 236 |
231 /* ================================================================== */ | 237 /* ================================================================== */ |
232 | 238 |
239 // based on llc code, University of Illinois Open Source License | |
233 void write_asm_to_file(llvm::Module& m, llvm::raw_fd_ostream& out) | 240 void write_asm_to_file(llvm::Module& m, llvm::raw_fd_ostream& out) |
234 { | 241 { |
235 using namespace llvm; | 242 using namespace llvm; |
236 | 243 |
237 std::string Err; | 244 std::string Err; |
288 assert(rmod); | 295 assert(rmod); |
289 } | 296 } |
290 | 297 |
291 /* ================================================================== */ | 298 /* ================================================================== */ |
292 | 299 |
300 // helper functions for gcc call to assemble | |
301 // based on llvm-ld code, University of Illinois Open Source License | |
302 | |
303 /// CopyEnv - This function takes an array of environment variables and makes a | |
304 /// copy of it. This copy can then be manipulated any way the caller likes | |
305 /// without affecting the process's real environment. | |
306 /// | |
307 /// Inputs: | |
308 /// envp - An array of C strings containing an environment. | |
309 /// | |
310 /// Return value: | |
311 /// NULL - An error occurred. | |
312 /// | |
313 /// Otherwise, a pointer to a new array of C strings is returned. Every string | |
314 /// in the array is a duplicate of the one in the original array (i.e. we do | |
315 /// not copy the char *'s from one array to another). | |
316 /// | |
317 static char ** CopyEnv(char ** const envp) { | |
318 // Count the number of entries in the old list; | |
319 unsigned entries; // The number of entries in the old environment list | |
320 for (entries = 0; envp[entries] != NULL; entries++) | |
321 /*empty*/; | |
322 | |
323 // Add one more entry for the NULL pointer that ends the list. | |
324 ++entries; | |
325 | |
326 // If there are no entries at all, just return NULL. | |
327 if (entries == 0) | |
328 return NULL; | |
329 | |
330 // Allocate a new environment list. | |
331 char **newenv = new char* [entries]; | |
332 if ((newenv = new char* [entries]) == NULL) | |
333 return NULL; | |
334 | |
335 // Make a copy of the list. Don't forget the NULL that ends the list. | |
336 entries = 0; | |
337 while (envp[entries] != NULL) { | |
338 newenv[entries] = new char[strlen (envp[entries]) + 1]; | |
339 strcpy (newenv[entries], envp[entries]); | |
340 ++entries; | |
341 } | |
342 newenv[entries] = NULL; | |
343 | |
344 return newenv; | |
345 } | |
346 | |
347 // based on llvm-ld code, University of Illinois Open Source License | |
348 | |
349 /// RemoveEnv - Remove the specified environment variable from the environment | |
350 /// array. | |
351 /// | |
352 /// Inputs: | |
353 /// name - The name of the variable to remove. It cannot be NULL. | |
354 /// envp - The array of environment variables. It cannot be NULL. | |
355 /// | |
356 /// Notes: | |
357 /// This is mainly done because functions to remove items from the environment | |
358 /// are not available across all platforms. In particular, Solaris does not | |
359 /// seem to have an unsetenv() function or a setenv() function (or they are | |
360 /// undocumented if they do exist). | |
361 /// | |
362 static void RemoveEnv(const char * name, char ** const envp) { | |
363 for (unsigned index=0; envp[index] != NULL; index++) { | |
364 // Find the first equals sign in the array and make it an EOS character. | |
365 char *p = strchr (envp[index], '='); | |
366 if (p == NULL) | |
367 continue; | |
368 else | |
369 *p = '\0'; | |
370 | |
371 // Compare the two strings. If they are equal, zap this string. | |
372 // Otherwise, restore it. | |
373 if (!strcmp(name, envp[index])) | |
374 *envp[index] = '\0'; | |
375 else | |
376 *p = '='; | |
377 } | |
378 | |
379 return; | |
380 } | |
381 | |
382 // uses gcc to make an obj out of an assembly file | |
383 // based on llvm-ld code, University of Illinois Open Source License | |
384 void assemble(const llvm::sys::Path& asmpath, const llvm::sys::Path& objpath, char** envp) | |
385 { | |
386 using namespace llvm; | |
387 | |
388 sys::Path gcc = llvm::sys::Program::FindProgramByName("gcc"); | |
389 | |
390 // Remove these environment variables from the environment of the | |
391 // programs that we will execute. It appears that GCC sets these | |
392 // environment variables so that the programs it uses can configure | |
393 // themselves identically. | |
394 // | |
395 // However, when we invoke GCC below, we want it to use its normal | |
396 // configuration. Hence, we must sanitize its environment. | |
397 char ** clean_env = CopyEnv(envp); | |
398 if (clean_env == NULL) | |
399 return; | |
400 RemoveEnv("LIBRARY_PATH", clean_env); | |
401 RemoveEnv("COLLECT_GCC_OPTIONS", clean_env); | |
402 RemoveEnv("GCC_EXEC_PREFIX", clean_env); | |
403 RemoveEnv("COMPILER_PATH", clean_env); | |
404 RemoveEnv("COLLECT_GCC", clean_env); | |
405 | |
406 | |
407 // Run GCC to assemble and link the program into native code. | |
408 // | |
409 // Note: | |
410 // We can't just assemble and link the file with the system assembler | |
411 // and linker because we don't know where to put the _start symbol. | |
412 // GCC mysteriously knows how to do it. | |
413 std::vector<std::string> args; | |
414 args.push_back(gcc.toString()); | |
415 args.push_back("-fno-strict-aliasing"); | |
416 args.push_back("-O3"); | |
417 args.push_back("-c"); | |
418 args.push_back("-xassembler"); | |
419 args.push_back(asmpath.toString()); | |
420 args.push_back("-o"); | |
421 args.push_back(objpath.toString()); | |
422 | |
423 //TODO: Add other options, like -fpic | |
424 | |
425 // Now that "args" owns all the std::strings for the arguments, call the c_str | |
426 // method to get the underlying string array. We do this game so that the | |
427 // std::string array is guaranteed to outlive the const char* array. | |
428 std::vector<const char *> Args; | |
429 for (unsigned i = 0, e = args.size(); i != e; ++i) | |
430 Args.push_back(args[i].c_str()); | |
431 Args.push_back(0); | |
432 | |
433 Logger::println("Assembling with: "); | |
434 std::vector<const char*>::const_iterator I = Args.begin(), E = Args.end(); | |
435 std::ostream& logstr = Logger::cout(); | |
436 for (; I != E; ++I) | |
437 if (*I) | |
438 logstr << "'" << *I << "'" << " "; | |
439 logstr << "\n" << std::flush; | |
440 | |
441 // Run the compiler to assembly the program. | |
442 std::string ErrMsg; | |
443 int R = sys::Program::ExecuteAndWait( | |
444 gcc, &Args[0], (const char**)clean_env, 0, 0, 0, &ErrMsg); | |
445 delete [] clean_env; | |
446 } | |
447 | |
448 | |
449 /* ================================================================== */ | |
293 // build module ctor | 450 // build module ctor |
294 | 451 |
295 static llvm::Function* build_module_ctor() | 452 llvm::Function* build_module_ctor() |
296 { | 453 { |
297 if (gIR->ctors.empty()) | 454 if (gIR->ctors.empty()) |
298 return NULL; | 455 return NULL; |
299 | 456 |
300 size_t n = gIR->ctors.size(); | 457 size_t n = gIR->ctors.size(); |