def compile_and_link(self, src, dst): """Compile and link a file directly.""" cmd = [self.get_command(), src, "-o", dst] + self.__standard + self.__compiler_flags + self._compiler_flags_extra + self._definitions + \ self._include_directories + self.get_linker_flags() + self.get_library_directory_list() + self.get_library_list() (so, se) = run_command(cmd) if 0 < len(se) and is_verbose(): print(se)
def compile_asm(self, src, dst, whole_program=False): """Compile a file into assembler source.""" cmd = [self.get_command(), "-S", src, "-o", dst] + self.__standard + self.__compiler_flags + \ self._compiler_flags_extra + self._definitions + self._include_directories if whole_program: cmd += self.__compiler_flags_whole_program (so, se) = run_command(cmd) if 0 < len(se) and is_verbose(): print(se)
def crunch_entry_push(self, op): """Crunch amd64/ia32 push directives from given line listing.""" lst = self.want_label(op) if not lst: return ii = lst[0] + 1 jj = ii stack_decrement = 0 stack_save_decrement = 0 reinstated_lines = [] while True: current_line = self.__content[jj] match = re.match(r'\s*(push\w).*%(\w+)', current_line, re.IGNORECASE) if match: if is_stack_save_register(match.group(2)): stack_save_decrement += get_push_size(match.group(1)) else: stack_decrement += get_push_size(match.group(1)) jj += 1 continue # Preserve comment lines as they are. match = re.match(r'^\s*[#;].*', current_line, re.IGNORECASE) if match: reinstated_lines += [current_line] jj += 1 continue # Saving stack pointer or sometimes initializing edx seem to be within pushing. match = re.match(r'\s*mov\w\s+%\w+,\s*%(rbp|ebp|edx).*', current_line, re.IGNORECASE) if match: if is_stack_save_register(match.group(1)): stack_save_decrement = 0 reinstated_lines += [current_line] jj += 1 continue # Some types of lines can be in the middle of pushing. if is_reinstate_line(current_line): reinstated_lines += [current_line] jj += 1 continue match = re.match(r'\s*sub.*\s+[^\d]*(\d+),\s*%(rsp|esp)', current_line, re.IGNORECASE) if match: total_decrement = int( match.group(1)) + stack_decrement + stack_save_decrement self.__content[jj] = re.sub(r'\d+', str(total_decrement), current_line) break if is_verbose(): print("Erasing function header from '%s': %i lines" % (op, jj - ii - len(reinstated_lines))) self.erase(ii, jj) self.__content[ii:ii] = reinstated_lines
def write(self): """Write compressed output.""" fd = open(self.__output_name, "w") if not fd: raise RuntimeError("could not write GLSL header '%s'" % (self.__output_name)) fd.write(self.generateHeaderOutput()) fd.close() if is_verbose(): print("Wrote GLSL header: '%s' => '%s'" % (self.__variable_name, self.__output_name))
def format(self, force): """Return formatted output.""" ret = "." if self.__swizzle: if not self.__swizzle_export: if force: if is_verbose(): print("WARNING: %s swizzle status unconfirmed" % (str(self))) return ret + self.__name.format(force) return "" return ret + self.generateSwizzle() return ret + self.__name.format(force)
def __init__(self, lst, explicit): """Constructor.""" GlslBlock.__init__(self) self.__explicit = explicit self.__squashable = False self.__allow_squash = False # Check for degenerate scope. if (1 == len(lst)) and is_glsl_block_declaration(lst[0]): raise RuntimeError("scope with only block '%s' is degenerate" % (lst[0].format(True))) # Check for empty scope (likely an error). if 0 >= len(lst): if is_verbose(): print("WARNING: empty scope") # Hierarchy. self.addChildren(lst)
def crunch_jump_pop(self, op): """Crunch popping before a jump.""" lst = self.want_line(r'\s*(jmp\s+%s)\s+.*' % (op)) if not lst: return ii = lst[0] jj = ii - 1 while True: if (0 > jj) or not re.match(r'\s*(pop\S).*', self.__content[jj], re.IGNORECASE): if is_verbose(): print( "Erasing function footer before jump to '%s': %i lines" % (op, ii - jj - 1)) self.erase(jj + 1, ii) break jj -= 1
def crunch_amd64(self, lst): """Perform platform-dependent crunching.""" self.crunch_entry_push("_start") self.crunch_entry_push(ELFLING_UNCOMPRESSED) self.crunch_jump_pop(ELFLING_UNCOMPRESSED) lst = self.want_line(r'\s*(int\s+\$0x3|syscall)\s+.*') if lst: ii = lst[0] + 1 jj = ii while True: if len(self.__content) <= jj or re.match( r'\s*\S+\:\s*', self.__content[jj]): if is_verbose(): print("Erasing function footer after '%s': %i lines" % (lst[1], jj - ii)) self.erase(ii, jj) break jj += 1
def selectSwizzle(self): counted = self.count() rgba = 0 if "r" in counted: rgba += counted["r"] if "g" in counted: rgba += counted["g"] if "b" in counted: rgba += counted["b"] if "a" in counted: rgba += counted["a"] stpq = 0 if "s" in counted: stpq += counted["s"] if "t" in counted: stpq += counted["t"] if "p" in counted: stpq += counted["p"] if "q" in counted: stpq += counted["q"] xyzw = 0 if "x" in counted: xyzw += counted["x"] if "y" in counted: xyzw += counted["y"] if "z" in counted: xyzw += counted["z"] if "w" in counted: xyzw += counted["w"] if (xyzw >= rgba) and (xyzw >= stpq): ret = ("x", "y", "z", "w") selected_for = xyzw selected_against = "rgba: %i, stpq: %i" % (rgba, stpq) elif (stpq >= xyzw) and (stpq >= rgba): ret = ("s", "t", "p", "q") selected_for = stpq selected_against = "rgba: %i, xyzw: %i" % (rgba, xyzw) else: ret = ("r", "g", "b", "a") selected_for = rgba selected_against = "stpq: %i, xyzw: %i" % (stpq, xyzw) if is_verbose(): print("Selected GLSL swizzle: %s (%i vs. %s)" % (str(ret), selected_for, selected_against)) return ret
def detectType(self): """Try to detect type from filename.""" self.__type = None stub = r'.*[._\-\s]%s[._\-\s].*' if re.match(stub % ("frag"), self.__filename, re.I) or re.match( stub % ("fragment"), self.__filename, re.I): self.__type = "fragment" elif re.match(stub % ("geom"), self.__filename, re.I) or re.match( stub % ("geometry"), self.__filename, re.I): self.__type = "geometry" elif re.match(stub % ("vert"), self.__filename, re.I) or re.match( stub % ("vertex"), self.__filename, re.I): self.__type = "vertex" if is_verbose(): output_message = "Shader file '%s' type" % (self.__filename) if self.__type: print(output_message + (": '%s'" % (self.__type))) else: print(output_message + " not detected, assuming generic.")
def minimal_align(self): """Remove all .align declarations, replace with desired alignment.""" desired = int(PlatformVar("align")) adjustments = [] for ii in range(len(self.__content)): line = self.__content[ii] match = re.match(r'(\s*)\.align\s+(\d+).*', line) if not match: continue # Get actual align byte count. align = get_align_bytes(int(match.group(2))) if align == desired: continue # Some alignment directives are necessary due to data access. if not can_minimize_align(align): continue self.__content[ii] = "%s.balign %i\n" % (match.group(1), desired) adjustments += ["%i -> %i" % (align, desired)] if is_verbose() and adjustments: print("Alignment adjustment(%s): %s" % (self.get_name(), ", ".join(adjustments)))
def merge(self, op): """Attempt to merge with given segment.""" highest_mergable = 0 (head_src, bytestream_src) = self.deconstruct_tail() (bytestream_dst, tail_dst) = op.deconstruct_head() for ii in range(min(len(bytestream_src), len(bytestream_dst))): mergable = True for jj in range(ii + 1): if not bytestream_src[-ii - 1 + jj].mergable(bytestream_dst[jj]): mergable = False break if mergable: highest_mergable = ii + 1 if 0 >= highest_mergable: return False if is_verbose(): print("Merging headers %s and %s at %i bytes." % (self.__name, op.__name, highest_mergable)) for ii in range(highest_mergable): bytestream_src[-highest_mergable + ii].merge(bytestream_dst[ii]) bytestream_dst[0:highest_mergable] = [] self.reconstruct(head_src + bytestream_src) op.reconstruct(bytestream_dst + tail_dst) return True
def crunch(self, mode="full", max_inlines=-1, max_renames=-1, max_simplifys=-1): """Crunch the source code to smaller state.""" combines = None inlines = None renames = None simplifys = None # Expand unless crunching completely disabled. if "none" != mode: for ii in self.__sources: ii.expandRecursive() # Perform inlining passes. inlines = 0 while True: inline_pass_rv = self.inlinePass((max_inlines < 0) or (inlines < max_inlines)) # Last pass will return a listing of merged variable names. if is_listing(inline_pass_rv): merged = inline_pass_rv break # Inlining was done, another round. inlines += 1 # Perform simplification passes. simplifys = 0 for ii in self.__sources: if (0 <= max_simplifys) and (simplifys >= max_simplifys): break simplifys += simplify_pass(ii, max_simplifys - simplifys) # After all names have been collected, it's possible to select the best swizzle. swizzle = self.selectSwizzle() for ii in self.__sources: ii.selectSwizzle(swizzle) # Print number of inout merges. if is_verbose(): inout_merges = [] for ii in merged: block = ii[0] if is_listing(block): inout_merges += [block[0]] if inout_merges: print("GLSL inout connections found: %s" % (str(list(map(str, inout_merges))))) # Run rename passes until done. renames = 0 for ii in merged: if (0 <= max_renames) and (renames >= max_renames): break self.renamePass(ii[0], ii[1:]) renames += 1 # Run member rename passes until done. for ii in merged: block = ii[0] if is_listing(block): block = block[0] if not is_glsl_block_inout_struct(block): continue renames += self.renameMembers(block, max_renames - renames) # Also rename block type. if (0 > max_renames) or (renames < max_renames): self.renameBlock(ii[0]) renames += 1 # Perform recombine passes. for ii in self.__sources: combines = ii.collapseRecursive(mode) # Print summary of operations. if is_verbose(): operations = [] if inlines: operations += ["%i inlines" % (inlines)] if simplifys: operations += ["%i simplifys" % (simplifys)] if renames: operations += ["%i renames" % (renames)] if combines: operations += ["%i combines" % (combines)] if operations: print("GLSL processing done: %s" % (", ".join(operations)))