File: offline_linker.cpp

package info (click to toggle)
intel-compute-runtime 25.44.36015.8-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 79,632 kB
  • sloc: cpp: 931,547; lisp: 2,074; sh: 719; makefile: 162; python: 21
file content (353 lines) | stat: -rw-r--r-- 13,405 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
 * Copyright (C) 2022-2024 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#include "offline_linker.h"

#include "shared/offline_compiler/source/ocloc_api.h"
#include "shared/offline_compiler/source/ocloc_arg_helper.h"
#include "shared/offline_compiler/source/ocloc_igc_facade.h"
#include "shared/source/compiler_interface/intermediate_representations.h"
#include "shared/source/device_binary_format/elf/elf_encoder.h"
#include "shared/source/device_binary_format/elf/ocl_elf.h"
#include "shared/source/helpers/compiler_product_helper.h"
#include "shared/source/helpers/string.h"
#include "shared/source/release_helper/release_helper.h"

#include "cif/common/cif_main.h"
#include "cif/import/library_api.h"
#include "ocl_igc_interface/igc_ocl_device_ctx.h"
#include "ocl_igc_interface/platform_helper.h"

#include <algorithm>
#include <array>

namespace NEO {

CIF::CIFMain *createMainNoSanitize(CIF::CreateCIFMainFunc_t createFunc);

std::unique_ptr<OfflineLinker> OfflineLinker::create(size_t argsCount, const std::vector<std::string> &args, int &errorCode, OclocArgHelper *argHelper) {
    std::unique_ptr<OfflineLinker> linker{new OfflineLinker{argHelper, std::make_unique<OclocIgcFacade>(argHelper)}};
    errorCode = linker->initialize(argsCount, args);

    return linker;
}

OfflineLinker::OfflineLinker(OclocArgHelper *argHelper, std::unique_ptr<OclocIgcFacade> igcFacade)
    : argHelper{argHelper}, operationMode{OperationMode::skipExecution}, outputFilename{"linker_output"}, outputFormat{IGC::CodeType::llvmBc}, igcFacade{std::move(igcFacade)} {}

OfflineLinker::~OfflineLinker() = default;

int OfflineLinker::initialize(size_t argsCount, const std::vector<std::string> &args) {
    const auto parsingResult{parseCommand(argsCount, args)};
    if (parsingResult != OCLOC_SUCCESS) {
        return parsingResult;
    }

    // If a user requested help, then stop here.
    if (operationMode == OperationMode::showHelp) {
        return OCLOC_SUCCESS;
    }

    const auto verificationResult{verifyLinkerCommand()};
    if (verificationResult != OCLOC_SUCCESS) {
        return verificationResult;
    }

    const auto loadingResult{loadInputFilesContent()};
    if (loadingResult != OCLOC_SUCCESS) {
        return loadingResult;
    }

    const auto hwInfoInitializationResult{initHardwareInfo()};
    if (hwInfoInitializationResult != OCLOC_SUCCESS) {
        return hwInfoInitializationResult;
    }

    const auto igcPreparationResult{igcFacade->initialize(hwInfo)};
    if (igcPreparationResult != OCLOC_SUCCESS) {
        return igcPreparationResult;
    }

    operationMode = OperationMode::linkFiles;
    return OCLOC_SUCCESS;
}

int OfflineLinker::parseCommand(size_t argsCount, const std::vector<std::string> &args) {
    if (argsCount < 2u) {
        operationMode = OperationMode::showHelp;
        return OCLOC_INVALID_COMMAND_LINE;
    }

    for (size_t argIndex = 1u; argIndex < argsCount; ++argIndex) {
        const auto &currentArg{args[argIndex]};
        const auto hasMoreArgs{argIndex + 1 < argsCount};

        if (currentArg == "link") {
            continue;
        } else if ((currentArg == "-file") && hasMoreArgs) {
            inputFilenames.push_back(args[argIndex + 1]);
            ++argIndex;
        } else if (currentArg == "-out" && hasMoreArgs) {
            outputFilename = args[argIndex + 1];
            ++argIndex;
        } else if ((currentArg == "-out_format") && hasMoreArgs) {
            outputFormat = parseOutputFormat(args[argIndex + 1]);
            ++argIndex;
        } else if ((currentArg == "-options") && hasMoreArgs) {
            options = args[argIndex + 1];
            ++argIndex;
        } else if ((currentArg == "-internal_options") && hasMoreArgs) {
            internalOptions = args[argIndex + 1];
            ++argIndex;
        } else if (currentArg == "--help") {
            operationMode = OperationMode::showHelp;
            return OCLOC_SUCCESS;
        } else {
            argHelper->printf("Invalid option (arg %zd): %s\n", argIndex, currentArg.c_str());
            return OCLOC_INVALID_COMMAND_LINE;
        }
    }

    return OCLOC_SUCCESS;
}

IGC::CodeType::CodeType_t OfflineLinker::parseOutputFormat(const std::string &outputFormatName) {
    constexpr static std::array supportedFormatNames = {
        std::pair{"ELF", IGC::CodeType::elf},
        std::pair{"LLVM_BC", IGC::CodeType::llvmBc}};

    for (const auto &[name, format] : supportedFormatNames) {
        if (name == outputFormatName) {
            return format;
        }
    }

    return IGC::CodeType::invalid;
}

int OfflineLinker::verifyLinkerCommand() {
    if (inputFilenames.empty()) {
        argHelper->printf("Error: Input name is missing! At least one input file is required!\n");
        return OCLOC_INVALID_COMMAND_LINE;
    }

    for (const auto &filename : inputFilenames) {
        if (filename.empty()) {
            argHelper->printf("Error: Empty filename cannot be used!\n");
            return OCLOC_INVALID_COMMAND_LINE;
        }

        if (!argHelper->fileExists(filename)) {
            argHelper->printf("Error: Input file %s missing.\n", filename.c_str());
            return OCLOC_INVALID_FILE;
        }
    }

    if (outputFormat == IGC::CodeType::invalid) {
        argHelper->printf("Error: Invalid output type!\n");
        return OCLOC_INVALID_COMMAND_LINE;
    }

    return OCLOC_SUCCESS;
}

int OfflineLinker::loadInputFilesContent() {
    std::unique_ptr<char[]> bytes{};
    size_t size{};
    IGC::CodeType::CodeType_t codeType{};

    inputFilesContent.reserve(inputFilenames.size());

    for (const auto &filename : inputFilenames) {
        size = 0;
        bytes = argHelper->loadDataFromFile(filename, size);
        if (size == 0) {
            argHelper->printf("Error: Cannot read input file: %s\n", filename.c_str());
            return OCLOC_INVALID_FILE;
        }

        codeType = detectCodeType(bytes.get(), size);
        if (codeType == IGC::CodeType::invalid) {
            argHelper->printf("Error: Unsupported format of input file: %s\n", filename.c_str());
            return OCLOC_INVALID_PROGRAM;
        }

        inputFilesContent.emplace_back(std::move(bytes), size, codeType);
    }

    return OCLOC_SUCCESS;
}

IGC::CodeType::CodeType_t OfflineLinker::detectCodeType(char *bytes, size_t size) const {
    const auto bytesArray = ArrayRef<const uint8_t>::fromAny(bytes, size);
    if (isSpirVBitcode(bytesArray)) {
        return IGC::CodeType::spirV;
    }

    if (isLlvmBitcode(bytesArray)) {
        return IGC::CodeType::llvmBc;
    }

    return IGC::CodeType::invalid;
}

int OfflineLinker::initHardwareInfo() {
    // In spite of linking input files to intermediate representation instead of native binaries,
    // we have to initialize hardware info. Without that, initialization of IGC fails.
    // Therefore, we select the first valid hardware info entry and use it.
    const auto hwInfoTable{getHardwareInfoTable()};
    for (auto productId = 0u; productId < hwInfoTable.size(); ++productId) {
        if (hwInfoTable[productId]) {
            hwInfo = *hwInfoTable[productId];

            auto compilerProductHelper = NEO::CompilerProductHelper::create(hwInfo.platform.eProductFamily);
            UNRECOVERABLE_IF(compilerProductHelper == nullptr);
            hwInfo.ipVersion = compilerProductHelper->getHwIpVersion(hwInfo);
            auto releaseHelper = NEO::ReleaseHelper::create(hwInfo.ipVersion);
            const auto hwInfoConfig = compilerProductHelper->getHwInfoConfig(hwInfo);
            setHwInfoValuesFromConfig(hwInfoConfig, hwInfo);
            hardwareInfoSetup[hwInfo.platform.eProductFamily](&hwInfo, true, hwInfoConfig, releaseHelper.get());

            return OCLOC_SUCCESS;
        }
    }

    argHelper->printf("Error! Cannot retrieve any valid hardware information!\n");
    return OCLOC_INVALID_DEVICE;
}

ArrayRef<const HardwareInfo *> OfflineLinker::getHardwareInfoTable() const {
    return {hardwareInfoTable};
}

int OfflineLinker::execute() {
    switch (operationMode) {
    case OperationMode::showHelp:
        return showHelp();
    case OperationMode::linkFiles:
        return link();
    case OperationMode::skipExecution:
        [[fallthrough]];
    default:
        argHelper->printf("Error: Linker cannot be executed due to unsuccessful initialization!\n");
        return OCLOC_INVALID_COMMAND_LINE;
    }
}

int OfflineLinker::showHelp() {
    constexpr auto help{R"===(Links several IR files to selected output format (LLVM BC, ELF).
Input files can be given in SPIR-V or LLVM BC.

Usage: ocloc link [-file <filename>]... -out <filename> [-out_format <format>] [-options <options>] [-internal_options <options>] [--help]

  -file <filename>              The input file to be linked.
                                Multiple files can be passed using repetition of this arguments.
                                Please see examples below.

  -out <filename>               Output filename.

  -out_format <format>          Output file format. Supported ones are ELF and LLVM_BC.
                                When not specified, LLVM_BC is used.

  -options <options>            Optional OpenCL C compilation options
                                as defined by OpenCL specification.

  -internal_options <options>   Optional compiler internal options
                                as defined by compilers used underneath.
                                Check intel-graphics-compiler (IGC) project
                                for details on available internal options.
                                You also may provide explicit --help to inquire
                                information about option, mentioned in -options.

  --help                        Print this usage message.

Examples:
  Link two SPIR-V files to LLVM BC output
    ocloc link -file first_file.spv -file second_file.spv -out linker_output.llvmbc

  Link two LLVM BC files to ELF output
    ocloc link -file first_file.llvmbc -file second_file.llvmbc -out_format ELF -out translated.elf
)==="};

    argHelper->printf(help);

    return OCLOC_SUCCESS;
}

int OfflineLinker::link() {
    const auto encodedElfFile{createSingleInputFile()};
    if (outputFormat == IGC::CodeType::elf) {
        argHelper->saveOutput(outputFilename, encodedElfFile.data(), encodedElfFile.size());
        return OCLOC_SUCCESS;
    }

    const auto [translationResult, translatedBitcode] = translateToOutputFormat(encodedElfFile);
    if (translationResult == OCLOC_SUCCESS) {
        argHelper->saveOutput(outputFilename, translatedBitcode.data(), translatedBitcode.size());
    }

    return translationResult;
}

std::vector<uint8_t> OfflineLinker::createSingleInputFile() const {
    NEO::Elf::ElfEncoder<> elfEncoder{true, false, 1U};
    elfEncoder.getElfFileHeader().type = Elf::ET_OPENCL_OBJECTS;

    for (const auto &[bytes, size, codeType] : inputFilesContent) {
        const auto isSpirv = codeType == IGC::CodeType::spirV;
        const auto sectionType = isSpirv ? Elf::SHT_OPENCL_SPIRV : Elf::SHT_OPENCL_LLVM_BINARY;
        const auto sectionName = isSpirv ? Elf::SectionNamesOpenCl::spirvObject : Elf::SectionNamesOpenCl::llvmObject;
        const auto bytesArray = ArrayRef<const uint8_t>::fromAny(bytes.get(), size);

        elfEncoder.appendSection(sectionType, sectionName, bytesArray);
    }

    return elfEncoder.encode();
}

std::pair<int, std::vector<uint8_t>> OfflineLinker::translateToOutputFormat(const std::vector<uint8_t> &elfInput) {
    auto igcSrc = igcFacade->createConstBuffer(elfInput.data(), elfInput.size());
    auto igcOptions = igcFacade->createConstBuffer(options.c_str(), options.size());
    auto igcInternalOptions = igcFacade->createConstBuffer(internalOptions.c_str(), internalOptions.size());
    auto igcTranslationCtx = igcFacade->createTranslationContext(IGC::CodeType::elf, outputFormat);

    const auto tracingOptions{nullptr};
    const auto tracingOptionsSize{0};
    const auto igcOutput = igcTranslationCtx->Translate(igcSrc.get(), igcOptions.get(), igcInternalOptions.get(), tracingOptions, tracingOptionsSize);

    std::vector<uint8_t> outputFileContent{};
    if (!igcOutput) {
        argHelper->printf("Error: Translation has failed! IGC output is nullptr!\n");
        return {OCLOC_OUT_OF_HOST_MEMORY, std::move(outputFileContent)};
    }

    if (igcOutput->GetOutput()->GetSizeRaw() != 0) {
        outputFileContent.resize(igcOutput->GetOutput()->GetSizeRaw());
        memcpy_s(outputFileContent.data(), outputFileContent.size(), igcOutput->GetOutput()->GetMemory<char>(), igcOutput->GetOutput()->GetSizeRaw());
    }

    tryToStoreBuildLog(igcOutput->GetBuildLog()->GetMemory<char>(), igcOutput->GetBuildLog()->GetSizeRaw());

    const auto errorCode{igcOutput->Successful() && !outputFileContent.empty() ? OCLOC_SUCCESS : OCLOC_BUILD_PROGRAM_FAILURE};
    if (errorCode != OCLOC_SUCCESS) {
        argHelper->printf("Error: Translation has failed! IGC returned empty output.\n");
    }

    return {errorCode, std::move(outputFileContent)};
}

std::string OfflineLinker::getBuildLog() const {
    return buildLog;
}

void OfflineLinker::tryToStoreBuildLog(const char *buildLogRaw, size_t size) {
    if (buildLogRaw && size != 0) {
        buildLog = std::string{buildLogRaw, buildLogRaw + size};
    }
}

} // namespace NEO