if (Section.Relocations.empty())
return;
size_t Start = getInputSectionOffset();
- size_t Size = getSize();
+ size_t Size = getInputSize();
for (const WasmRelocation &R : Section.Relocations)
if (R.Offset >= Start && R.Offset < Start + Size)
Relocations.push_back(R);
assert(!hasTableIndex());
TableIndex = Index;
}
+
+// Write a relocation value without padding and return the number of bytes
+// witten.
+static unsigned writeCompressedReloc(uint8_t *Buf, const WasmRelocation &Rel,
+ uint32_t Value) {
+ switch (Rel.Type) {
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ return encodeULEB128(Value, Buf);
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ return encodeSLEB128(static_cast<int32_t>(Value), Buf);
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ return 4;
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
+static unsigned getRelocWidthPadded(const WasmRelocation &Rel) {
+ switch (Rel.Type) {
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ return 5;
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ return 4;
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
+static unsigned getRelocWidth(const WasmRelocation &Rel, uint32_t Value) {
+ uint8_t Buf[5];
+ return writeCompressedReloc(Buf, Rel, Value);
+}
+
+// Relocations of type LEB and SLEB in the code section are padded to 5 bytes
+// so that a fast linker can blindly overwrite them without needing to worry
+// about the number of bytes needed to encode the values.
+// However, for optimal output the code section can be compressed to remove
+// the padding then outputting non-relocatable files.
+// In this case we need to perform a size calculation based on the value at each
+// relocation. At best we end up saving 4 bytes for each relocation entry.
+//
+// This function only computes the final output size. It must be called
+// before getSize() is used to calculate of layout of the code section.
+void InputFunction::calculateSize() {
+ if (!File || !Config->CompressRelocTargets)
+ return;
+
+ DEBUG(dbgs() << "calculateSize: " << getName() << "\n");
+
+ const uint8_t *SecStart = File->CodeSection->Content.data();
+ const uint8_t *FuncStart = SecStart + getInputSectionOffset();
+ uint32_t FunctionSizeLength;
+ decodeULEB128(FuncStart, &FunctionSizeLength);
+
+ uint32_t Start = getInputSectionOffset();
+ uint32_t End = Start + Function->Size;
+
+ uint32_t LastRelocEnd = Start + FunctionSizeLength;
+ for (WasmRelocation &Rel : Relocations) {
+ DEBUG(dbgs() << " region: " << (Rel.Offset - LastRelocEnd) << "\n");
+ CompressedFuncSize += Rel.Offset - LastRelocEnd;
+ CompressedFuncSize += getRelocWidth(Rel, File->calcNewValue(Rel));
+ LastRelocEnd = Rel.Offset + getRelocWidthPadded(Rel);
+ }
+ DEBUG(dbgs() << " final region: " << (End - LastRelocEnd) << "\n");
+ CompressedFuncSize += End - LastRelocEnd;
+
+ // Now we know how long the resulting function is we can add the encoding
+ // of its length
+ uint8_t Buf[5];
+ CompressedSize = CompressedFuncSize + encodeULEB128(CompressedFuncSize, Buf);
+
+ DEBUG(dbgs() << " calculateSize orig: " << Function->Size << "\n");
+ DEBUG(dbgs() << " calculateSize new: " << CompressedSize << "\n");
+}
+
+// Override the default writeTo method so that we can (optionally) write the
+// compressed version of the function.
+void InputFunction::writeTo(uint8_t *Buf) const {
+ if (!File || !Config->CompressRelocTargets)
+ return InputChunk::writeTo(Buf);
+
+ Buf += OutputOffset;
+ uint8_t *Orig = Buf;
+
+ const uint8_t *SecStart = File->CodeSection->Content.data();
+ const uint8_t *FuncStart = SecStart + getInputSectionOffset();
+ const uint8_t *End = FuncStart + Function->Size;
+ uint32_t Count;
+ decodeULEB128(Buf, &Count);
+ FuncStart += Count;
+
+ DEBUG(dbgs() << "write func: " << getName() << "\n");
+ Buf += encodeULEB128(CompressedFuncSize, Buf);
+ const uint8_t *LastRelocEnd = FuncStart;
+ for (const WasmRelocation &Rel : Relocations) {
+ unsigned ChunkSize = (SecStart + Rel.Offset) - LastRelocEnd;
+ DEBUG(dbgs() << " write chunk: " << ChunkSize << "\n");
+ memcpy(Buf, LastRelocEnd, ChunkSize);
+ Buf += ChunkSize;
+ Buf += writeCompressedReloc(Buf, Rel, File->calcNewValue(Rel));
+ LastRelocEnd = SecStart + Rel.Offset + getRelocWidthPadded(Rel);
+ }
+
+ unsigned ChunkSize = End - LastRelocEnd;
+ DEBUG(dbgs() << " write final chunk: " << ChunkSize << "\n");
+ memcpy(Buf, LastRelocEnd, ChunkSize);
+ DEBUG(dbgs() << " total: " << (Buf + ChunkSize - Orig) << "\n");
+}
Kind kind() const { return SectionKind; }
- uint32_t getSize() const { return data().size(); }
+ virtual uint32_t getSize() const { return data().size(); }
void copyRelocations(const WasmSection &Section);
- void writeTo(uint8_t *SectionStart) const;
+ virtual void writeTo(uint8_t *SectionStart) const;
ArrayRef<WasmRelocation> getRelocations() const { return Relocations; }
virtual ~InputChunk() = default;
virtual ArrayRef<uint8_t> data() const = 0;
virtual uint32_t getInputSectionOffset() const = 0;
+ virtual uint32_t getInputSize() const { return getSize(); };
// Verifies the existing data at relocation targets matches our expectations.
// This is performed only debug builds as an extra sanity check.
C->kind() == InputChunk::SyntheticFunction;
}
+ void writeTo(uint8_t *SectionStart) const override;
StringRef getName() const override { return Function->SymbolName; }
StringRef getDebugName() const override { return Function->DebugName; }
uint32_t getComdat() const override { return Function->Comdat; }
uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
uint32_t getFunctionCodeOffset() const { return Function->CodeOffset; }
+ uint32_t getSize() const override {
+ if (Config->CompressRelocTargets && File) {
+ assert(CompressedSize);
+ return CompressedSize;
+ }
+ return data().size();
+ }
uint32_t getFunctionIndex() const { return FunctionIndex.getValue(); }
bool hasFunctionIndex() const { return FunctionIndex.hasValue(); }
void setFunctionIndex(uint32_t Index);
bool hasTableIndex() const { return TableIndex.hasValue(); }
void setTableIndex(uint32_t Index);
+ // The size of a given input function can depend on the values of the
+ // LEB relocations within it. This finalizeContents method is called after
+ // all the symbol values have be calcualted but before getSize() is ever
+ // called.
+ void calculateSize();
+
const WasmSignature &Signature;
protected:
ArrayRef<uint8_t> data() const override {
+ assert(!Config->CompressRelocTargets);
return File->CodeSection->Content.slice(getInputSectionOffset(),
Function->Size);
}
+
+ uint32_t getInputSize() const override { return Function->Size; }
+
uint32_t getInputSectionOffset() const override {
return Function->CodeSectionOffset;
}
const WasmFunction *Function;
llvm::Optional<uint32_t> FunctionIndex;
llvm::Optional<uint32_t> TableIndex;
+ uint32_t CompressedFuncSize = 0;
+ uint32_t CompressedSize = 0;
};
class SyntheticFunction : public InputFunction {