Index: include/llvm/CodeGen/StackMaps.h =================================================================== --- include/llvm/CodeGen/StackMaps.h +++ include/llvm/CodeGen/StackMaps.h @@ -19,6 +19,85 @@ #include namespace llvm { +/// Represents a location associated with a given record in the generated +/// stackmap section. +struct LocationRecord { + /// Can use StackMaps::Location::Type to interpret value of Type field. + uint8_t Type; + uint8_t SizeInBytes; + uint16_t DwarfRegNum; + int32_t Offset; + + void parse(uint8_t* data, unsigned& offset, const unsigned len); +}; + +/// Represents a given record (of a single stackmap, patchpoint, or statepoint +/// intrinsic) within the generated stackmap section. +struct StackMapRecord { + uint64_t PatchPointID; + uint32_t InstructionOffset; + uint16_t ReservedFlags; + std::vector Locations; //[NumLocations] + // LiveOuts omitted - could be added by interested users + + void parse(uint8_t* data, unsigned& offset, const unsigned len); +}; + +/// Represents information recorded about a given function within the text +/// section recorded in the stackmap section. In particular, used to record +/// the start of each function and it's stack size. +struct StackMapSizeRecord { + StackMapSizeRecord(uint64_t offset, uint64_t size) + : FunctionAddr(offset), StackSize(size) {} + uint64_t FunctionAddr; + uint64_t StackSize; + + /// Does this function have a fixed size frame? If not, the StackSize field + /// is undefined and meaningless. + bool isFixedSizeFrame() const; +}; + +/// Represents the contents of a generated stackmap section. Intended usage is +/// to construct an instance of this class on the stack, parse the memory +/// holding the stackmap section using the given API, and then access the data +/// through the provided fields. Note that this parser is intentionally and +/// deliberately version locked with the version of LLVM which includes it. No +/// effort is made to parse stackmap sections generated by other revisions. +/// Cross version parsing is explicitly and intentionally unsupported. +/// Note: The parsed data contains offset into the associated text section +/// before finalizeObject() is called. Once relocations are applied, the +/// offsets may not match the resulting code! +struct StackMapSection { +private: + void parse(uint8_t* data, unsigned& offset, const unsigned len); +public: + uint8_t Version; /* one expected */ + uint8_t Reserved8; /* zero expected */ + uint16_t Reserved16; /* zero expected */ + std::vector FnSizeRecords; + std::vector Constants; //[NumConstants] + std::vector Records; //[NumRecord] + + /// Parse the contents of a given region of memory. Can only be called on a + /// default initialized instance of this class. Attempting to parse multiple + /// sections using a single object is unsupported. + void parse(uint8_t* data, const unsigned len) { + unsigned offset = 0; + parse(data, offset, len); + assert(offset == len && "incomplete parsing of stack map section!"); + } + /// Dump a human readable form of the stackmap section previously parsed. + /// This is intended for debugging only. The format is not stable. + void dump() const; + + /// Given a particular address, return the record associated with it from the + /// parsed stack map section. Such a record must exist. + StackMapRecord& findRecordForRelPC(uint32_t RelPC); + + /// Check to see if a record exists for the given RelPC within the associated + /// text section. + bool hasRecordForRelPC(uint32_t RelPC); +}; class AsmPrinter; class MCExpr; Index: lib/CodeGen/StackMaps.cpp =================================================================== --- lib/CodeGen/StackMaps.cpp +++ lib/CodeGen/StackMaps.cpp @@ -34,6 +34,139 @@ const char *StackMaps::WSMP = "Stack Maps: "; + +template +static Primitive parse_primitive(uint8_t* data, unsigned& offset, + const unsigned len) { + assert(data && offset >= 0 && len >= 0); + assert(offset < len); + Primitive rval = *(Primitive*)(data + offset); + offset += sizeof(rval); + assert(offset <= len); + return rval; +} + +void LocationRecord::parse(uint8_t* data, unsigned& offset, + const unsigned len) { + assert(offset + sizeof(LocationRecord) <= len); + memcpy(this, data + offset, sizeof(LocationRecord)); + offset += sizeof(LocationRecord); + assert(Type <= 5); +} +void StackMapRecord::parse(uint8_t* data, unsigned& offset, + const unsigned len) { + PatchPointID = parse_primitive(data, offset, len); + InstructionOffset = parse_primitive(data, offset, len); + ReservedFlags = parse_primitive(data, offset, len); + assert(0 == ReservedFlags); + uint16_t NumLocations = parse_primitive(data, offset, len); + Locations.resize(NumLocations); + for (uint16_t i = 0; i < NumLocations; i++) { + Locations[i].parse(data, offset, len); + } + uint16_t Padding16 = parse_primitive(data, offset, len); + assert(Padding16 == 0); + uint16_t NumLiveOuts = parse_primitive(data, offset, len); + assert(NumLiveOuts == 0 && "need to implement live out parsing"); + + // If the offset is not 8-byte aligned, skip the padding inserted to align it. + if (offset % 8 != 0) { + offset += 8 - (offset % 8); + } +} + +bool StackMapSizeRecord::isFixedSizeFrame() const { + return StackSize != std::numeric_limits::max(); +} + +void StackMapSection::parse(uint8_t* data, unsigned& offset, + const unsigned len) { + Version = parse_primitive(data, offset, len); + Reserved8 = parse_primitive(data, offset, len); + Reserved16 = parse_primitive(data, offset, len); + assert(Version == 1); + assert(Reserved8 == 0); + assert(Reserved16 == 0); + + uint32_t NumFuncs = parse_primitive(data, offset, len); + uint32_t NumConstants = parse_primitive(data, offset, len); + uint32_t NumRecords = parse_primitive(data, offset, len); + for (uint32_t i = 0; i < NumFuncs; i++) { + uint64_t Addr = parse_primitive(data, offset, len); + uint64_t Size = parse_primitive(data, offset, len); + FnSizeRecords.push_back(StackMapSizeRecord(Addr, Size)); + } + for (uint32_t i = 0; i < NumConstants; i++) { + Constants.push_back(parse_primitive(data, offset, len)); + } + + Records.resize(NumRecords); + for (uint32_t i = 0; i < NumRecords; i++) { + Records[i].parse(data, offset, len); + } +} + +void StackMapSection::dump() const { + if (FnSizeRecords.empty()) { + printf("Functions (%d) []\n", static_cast(FnSizeRecords.size())); + } else { + printf("Functions (%d) [\n", static_cast(FnSizeRecords.size())); + for (uint16_t j = 0; j < FnSizeRecords.size(); j++) { + printf(" addr=%u, size=%u", static_cast(FnSizeRecords[j].FunctionAddr), + static_cast(FnSizeRecords[j].StackSize)); + } + puts("]"); + } + if (Constants.empty()) { + printf("Constants (%d) []\n", static_cast(Constants.size())); + } else { + printf("Constants (%d) [\n", static_cast(Constants.size())); + for (uint16_t j = 0; j < Constants.size(); j++) { + printf(" value=%u", static_cast(Constants[j])); + } + puts("]"); + } + printf("Records (%d) [\n", static_cast(Records.size())); + for (unsigned i = 0; i < Records.size(); i++) { + const StackMapRecord& Rec = Records[i]; + printf(" id=%lu, offset=%d, flags=%X\n", Rec.PatchPointID, Rec.InstructionOffset, + Rec.ReservedFlags); + if (Rec.Locations.empty()) { + printf(" Locations (%d) []\n", static_cast(Rec.Locations.size())); + } else { + printf(" Locations (%d) [\n", static_cast(Rec.Locations.size())); + for (uint16_t j = 0; j < Rec.Locations.size(); j++) { + const LocationRecord& Loc = Rec.Locations[j]; + printf(" type=%u, size=%u, dwarfreg=%u, offset=%u\n", Loc.Type, Loc.SizeInBytes, + Loc.DwarfRegNum, Loc.Offset); + } + puts(" ]"); + } + } + puts("]"); +} +StackMapRecord& StackMapSection::findRecordForRelPC(uint32_t RelPC) { + // brute force search for the moment, could be improved + for (unsigned i = 0; i < Records.size(); i++) { + StackMapRecord& Rec = Records[i]; + if (Rec.InstructionOffset == RelPC) { + return Rec; + } + } + report_fatal_error("no record for offset into text section"); + return *((StackMapRecord*)nullptr); +} +bool StackMapSection::hasRecordForRelPC(uint32_t RelPC) { + // brute force search for the moment, could be improved + for (unsigned i = 0; i < Records.size(); i++) { + StackMapRecord& Rec = Records[i]; + if (Rec.InstructionOffset == RelPC) { + return true; + } + } + return false; +} + PatchPointOpers::PatchPointOpers(const MachineInstr *MI) : MI(MI), HasDef(MI->getOperand(0).isReg() && MI->getOperand(0).isDef() &&