Skip to content

Commit 753f1ef

Browse files
authored
Simplify linker flag handling. NFC (#23515)
This change simplifies the interface between emcc.py (the compiler driver) and link.py (the linker). Linker flags are no longer stored in the EmccState or stored alongside their ordinal number. Instead passed directly the link.py as a simple list. This change brings us closer to de-coupling the linker process with the hope of creating a standalone emld entry point.
1 parent d743092 commit 753f1ef

File tree

2 files changed

+147
-150
lines changed

2 files changed

+147
-150
lines changed

emcc.py

Lines changed: 49 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -108,31 +108,24 @@ class Mode(Enum):
108108
COMPILE_AND_LINK = auto()
109109

110110

111+
class LinkFlag:
112+
"""Used to represent a linker flag.
113+
114+
The flag value is stored along with a bool that distingingishes input
115+
files from non-files.
116+
117+
A list of these is return by separate_linker_flags.
118+
"""
119+
def __init__(self, value, is_file):
120+
self.value = value
121+
self.is_file = is_file
122+
123+
111124
class EmccState:
112125
def __init__(self, args):
113126
self.mode = Mode.COMPILE_AND_LINK
114127
# Using tuple here to prevent accidental mutation
115128
self.orig_args = tuple(args)
116-
# List of link options paired with their position on the command line [(i, option), ...].
117-
self.link_flags = []
118-
self.lib_dirs = []
119-
120-
def has_link_flag(self, f):
121-
return f in [x for _, x in self.link_flags]
122-
123-
def add_link_flag(self, i, flag):
124-
if flag.startswith('-L'):
125-
self.lib_dirs.append(flag[2:])
126-
# Link flags should be adding in strictly ascending order
127-
assert not self.link_flags or i > self.link_flags[-1][0], self.link_flags
128-
self.link_flags.append((i, flag))
129-
130-
def append_link_flag(self, flag):
131-
if self.link_flags:
132-
index = self.link_flags[-1][0] + 1
133-
else:
134-
index = 1
135-
self.add_link_flag(index, flag)
136129

137130

138131
class EmccOptions:
@@ -190,6 +183,7 @@ def __init__(self):
190183
self.nostartfiles = False
191184
self.sanitize_minimal_runtime = False
192185
self.sanitize = set()
186+
self.lib_dirs = []
193187

194188

195189
def create_reproduce_file(name, args):
@@ -662,20 +656,21 @@ def run(args):
662656
if state.mode == Mode.POST_LINK_ONLY:
663657
if len(options.input_files) != 1:
664658
exit_with_error('--post-link requires a single input file')
665-
separate_linker_flags(state, newargs)
659+
linker_args = separate_linker_flags(newargs)[1]
660+
linker_args = [f.value for f in linker_args]
666661
# Delay import of link.py to avoid processing this file when only compiling
667662
from tools import link
668-
link.run_post_link(options.input_files[0], options, state)
663+
link.run_post_link(options.input_files[0], options, linker_args)
669664
return 0
670665

671666
# Compile source code to object files
672667
# When only compiling this function never returns.
673-
linker_inputs = phase_compile_inputs(options, state, newargs)
668+
linker_args = phase_compile_inputs(options, state, newargs)
674669

675670
if state.mode == Mode.COMPILE_AND_LINK:
676671
# Delay import of link.py to avoid processing this file when only compiling
677672
from tools import link
678-
return link.run(linker_inputs, options, state)
673+
return link.run(options, linker_args)
679674
else:
680675
logger.debug('stopping after compile phase')
681676
return 0
@@ -747,26 +742,21 @@ def phase_parse_arguments(state):
747742
return options, newargs
748743

749744

750-
def separate_linker_flags(state, newargs):
751-
"""Process argument list separating out input files, compiler flags
752-
and linker flags.
753-
754-
- Linker flags are stored in state.link_flags
755-
- Input files and compiler-only flags are returned as two separate lists.
745+
def separate_linker_flags(newargs):
746+
"""Process argument list separating out compiler args and linker args.
756747
757-
Both linker flags and input files are stored as pairs of (i, entry) where
758-
`i` is the orginal index in the command line arguments. This allow the two
759-
lists to be recombined in the correct order by the linker code (link.py).
760-
761-
Note that this index can have a fractional part for input arguments that
762-
expand into multiple processed arguments, as in -Wl,-f1,-f2.
748+
- Linker flags include input files and are returned a list of LinkFlag objects.
749+
- Compiler flags are those to be passed to `clang -c`.
763750
"""
764751

765752
if settings.RUNTIME_LINKED_LIBS:
766753
newargs += settings.RUNTIME_LINKED_LIBS
767754

768-
input_files = []
769755
compiler_args = []
756+
linker_args = []
757+
758+
def add_link_arg(flag, is_file=False):
759+
linker_args.append(LinkFlag(flag, is_file))
770760

771761
skip = False
772762
for i in range(len(newargs)):
@@ -789,30 +779,24 @@ def get_next_arg():
789779
# https://bugs.python.org/issue1311
790780
if not os.path.exists(arg) and arg not in (os.devnull, '-'):
791781
exit_with_error('%s: No such file or directory ("%s" was expected to be an input file, based on the commandline arguments provided)', arg, arg)
792-
input_files.append((i, arg))
782+
add_link_arg(arg, True)
793783
elif arg == '-z':
794-
state.add_link_flag(i, newargs[i])
795-
state.add_link_flag(i + 1, get_next_arg())
784+
add_link_arg(arg)
785+
add_link_arg(get_next_arg())
796786
elif arg.startswith('-Wl,'):
797-
# Multiple comma separated link flags can be specified. Create fake
798-
# fractional indices for these: -Wl,a,b,c,d at index 4 becomes:
799-
# (4, a), (4.25, b), (4.5, c), (4.75, d)
800-
link_flags_to_add = arg.split(',')[1:]
801-
for flag_index, flag in enumerate(link_flags_to_add):
802-
state.add_link_flag(i + float(flag_index) / len(link_flags_to_add), flag)
787+
for flag in arg.split(',')[1:]:
788+
add_link_arg(flag)
803789
elif arg == '-Xlinker':
804-
state.add_link_flag(i + 1, get_next_arg())
805-
elif arg == '-s':
806-
state.add_link_flag(i, newargs[i])
790+
add_link_arg(get_next_arg())
807791
elif arg == '-s' or arg.startswith(('-l', '-L', '--js-library=', '-z')):
808-
state.add_link_flag(i, arg)
792+
add_link_arg(arg)
809793
elif not arg.startswith('-o') and arg not in ('-nostdlib', '-nostartfiles', '-nolibc', '-nodefaultlibs', '-s'):
810794
# All other flags are for the compiler
811795
compiler_args.append(arg)
812796
if skip:
813797
compiler_args.append(get_next_arg())
814798

815-
return compiler_args, input_files
799+
return compiler_args, linker_args
816800

817801

818802
@ToolchainProfiler.profile_block('setup')
@@ -955,8 +939,7 @@ def get_clang_command_asm():
955939
# filter out the link flags
956940
assert state.mode == Mode.COMPILE_AND_LINK
957941
assert not options.dash_c
958-
compile_args, input_files = separate_linker_flags(state, newargs)
959-
linker_inputs = []
942+
compile_args, linker_args = separate_linker_flags(newargs)
960943
seen_names = {}
961944

962945
def uniquename(name):
@@ -967,10 +950,9 @@ def uniquename(name):
967950
def get_object_filename(input_file):
968951
return in_temp(shared.replace_suffix(uniquename(input_file), '.o'))
969952

970-
def compile_source_file(i, input_file):
953+
def compile_source_file(input_file):
971954
logger.debug(f'compiling source file: {input_file}')
972955
output_file = get_object_filename(input_file)
973-
linker_inputs.append((i, output_file))
974956
if get_file_suffix(input_file) in ASSEMBLY_EXTENSIONS:
975957
cmd = get_clang_command_asm()
976958
elif get_file_suffix(input_file) in PREPROCESSED_EXTENSIONS:
@@ -993,28 +975,29 @@ def compile_source_file(i, input_file):
993975
assert os.path.exists(output_file)
994976
if options.save_temps:
995977
shutil.copyfile(output_file, shared.unsuffixed_basename(input_file) + '.o')
978+
return output_file
996979

997-
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
998-
for i, input_file in input_files:
980+
# Compile input files individually to temporary locations.
981+
for arg in linker_args:
982+
if not arg.is_file:
983+
continue
984+
input_file = arg.value
999985
file_suffix = get_file_suffix(input_file)
1000986
if file_suffix in SOURCE_EXTENSIONS | ASSEMBLY_EXTENSIONS or (options.dash_c and file_suffix == '.bc'):
1001-
compile_source_file(i, input_file)
987+
arg.value = compile_source_file(input_file)
1002988
elif file_suffix in DYLIB_EXTENSIONS:
1003989
logger.debug(f'using shared library: {input_file}')
1004-
linker_inputs.append((i, input_file))
1005990
elif building.is_ar(input_file):
1006991
logger.debug(f'using static library: {input_file}')
1007-
linker_inputs.append((i, input_file))
1008992
elif options.input_language:
1009-
compile_source_file(i, input_file)
993+
arg.value = compile_source_file(input_file)
1010994
elif input_file == '-':
1011995
exit_with_error('-E or -x required when input is from standard input')
1012996
else:
1013997
# Default to assuming the inputs are object files and pass them to the linker
1014-
logger.debug(f'using object file: {input_file}')
1015-
linker_inputs.append((i, input_file))
998+
pass
1016999

1017-
return linker_inputs
1000+
return [f.value for f in linker_args]
10181001

10191002

10201003
def version_string():
@@ -1319,6 +1302,8 @@ def consume_arg_file():
13191302
'encountered. If this is to a local system header/library, it may '
13201303
'cause problems (local system files make sense for compiling natively '
13211304
'on your system, but not necessarily to JavaScript).')
1305+
if arg.startswith('-L'):
1306+
options.lib_dirs.append(path_name)
13221307
elif check_flag('--emrun'):
13231308
options.emrun = True
13241309
elif check_flag('--cpuprofiler'):

0 commit comments

Comments
 (0)