mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge pull request #16487 from jacobly0/llvm-builder
llvm: incremental Builder improvements
This commit is contained in:
commit
d21d1d4ba2
@ -294,7 +294,7 @@ pub fn evalZigProcess(
|
||||
s: *Step,
|
||||
argv: []const []const u8,
|
||||
prog_node: *std.Progress.Node,
|
||||
) ![]const u8 {
|
||||
) !?[]const u8 {
|
||||
assert(argv.len != 0);
|
||||
const b = s.owner;
|
||||
const arena = b.allocator;
|
||||
@ -423,6 +423,8 @@ pub fn evalZigProcess(
|
||||
});
|
||||
}
|
||||
|
||||
if (s.cast(Compile)) |compile| if (compile.emit_bin == .no_emit) return result;
|
||||
|
||||
return result orelse return s.fail(
|
||||
"the following command failed to communicate the compilation result:\n{s}",
|
||||
.{try allocPrintCmd(arena, null, argv)},
|
||||
|
||||
@ -1997,7 +1997,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||
try zig_args.append(resolved_args_file);
|
||||
}
|
||||
|
||||
const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
|
||||
const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
|
||||
error.NeedCompileErrorCheck => {
|
||||
assert(self.expect_errors.len != 0);
|
||||
try checkCompileErrors(self);
|
||||
@ -2005,10 +2005,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
const output_dir = fs.path.dirname(output_bin_path).?;
|
||||
|
||||
// Update generated files
|
||||
{
|
||||
if (maybe_output_bin_path) |output_bin_path| {
|
||||
const output_dir = fs.path.dirname(output_bin_path).?;
|
||||
|
||||
self.output_dirname_source.path = output_dir;
|
||||
|
||||
self.output_path_source.path = b.pathJoin(
|
||||
|
||||
@ -148,8 +148,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||
|
||||
const output_path = try step.evalZigProcess(argv_list.items, prog_node);
|
||||
|
||||
self.out_basename = fs.path.basename(output_path);
|
||||
const output_dir = fs.path.dirname(output_path).?;
|
||||
self.out_basename = fs.path.basename(output_path.?);
|
||||
const output_dir = fs.path.dirname(output_path.?).?;
|
||||
|
||||
self.output_file.path = try fs.path.join(
|
||||
b.allocator,
|
||||
|
||||
@ -1912,7 +1912,7 @@ pub const Target = struct {
|
||||
return switch (target.cpu.arch) {
|
||||
.amdgcn => 4,
|
||||
.x86 => switch (target.os.tag) {
|
||||
.windows => 4,
|
||||
.windows, .uefi => 4,
|
||||
else => 16,
|
||||
},
|
||||
.arm,
|
||||
@ -1931,8 +1931,6 @@ pub const Target = struct {
|
||||
.bpfel,
|
||||
.mips64,
|
||||
.mips64el,
|
||||
.powerpc64,
|
||||
.powerpc64le,
|
||||
.riscv32,
|
||||
.riscv64,
|
||||
.sparc64,
|
||||
@ -1941,6 +1939,12 @@ pub const Target = struct {
|
||||
.wasm32,
|
||||
.wasm64,
|
||||
=> 16,
|
||||
.powerpc64,
|
||||
.powerpc64le,
|
||||
=> switch (target.os.tag) {
|
||||
else => 8,
|
||||
.linux => 16,
|
||||
},
|
||||
else => @divExact(target.ptrBitWidth(), 8),
|
||||
};
|
||||
}
|
||||
|
||||
@ -1053,6 +1053,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
|
||||
buf.appendSliceAssumeCapacity(",");
|
||||
}
|
||||
}
|
||||
if (buf.items.len == 0) break :blk "";
|
||||
assert(mem.endsWith(u8, buf.items, ","));
|
||||
buf.items[buf.items.len - 1] = 0;
|
||||
buf.shrinkAndFree(buf.items.len);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -26,10 +26,13 @@ pub const Context = opaque {
|
||||
extern fn LLVMContextDispose(C: *Context) void;
|
||||
|
||||
pub const createEnumAttribute = LLVMCreateEnumAttribute;
|
||||
extern fn LLVMCreateEnumAttribute(*Context, KindID: c_uint, Val: u64) *Attribute;
|
||||
extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) *Attribute;
|
||||
|
||||
pub const createTypeAttribute = LLVMCreateTypeAttribute;
|
||||
extern fn LLVMCreateTypeAttribute(C: *Context, KindID: c_uint, Type: *Type) *Attribute;
|
||||
|
||||
pub const createStringAttribute = LLVMCreateStringAttribute;
|
||||
extern fn LLVMCreateStringAttribute(*Context, Key: [*]const u8, Key_Len: c_uint, Value: [*]const u8, Value_Len: c_uint) *Attribute;
|
||||
extern fn LLVMCreateStringAttribute(C: *Context, Key: [*]const u8, Key_Len: c_uint, Value: [*]const u8, Value_Len: c_uint) *Attribute;
|
||||
|
||||
pub const pointerType = LLVMPointerTypeInContext;
|
||||
extern fn LLVMPointerTypeInContext(C: *Context, AddressSpace: c_uint) *Type;
|
||||
@ -309,12 +312,18 @@ pub const Value = opaque {
|
||||
pub const setAlignment = LLVMSetAlignment;
|
||||
extern fn LLVMSetAlignment(V: *Value, Bytes: c_uint) void;
|
||||
|
||||
pub const getFunctionCallConv = LLVMGetFunctionCallConv;
|
||||
extern fn LLVMGetFunctionCallConv(Fn: *Value) CallConv;
|
||||
|
||||
pub const setFunctionCallConv = LLVMSetFunctionCallConv;
|
||||
extern fn LLVMSetFunctionCallConv(Fn: *Value, CC: CallConv) void;
|
||||
|
||||
pub const setInstructionCallConv = LLVMSetInstructionCallConv;
|
||||
extern fn LLVMSetInstructionCallConv(Instr: *Value, CC: CallConv) void;
|
||||
|
||||
pub const setTailCallKind = ZigLLVMSetTailCallKind;
|
||||
extern fn ZigLLVMSetTailCallKind(CallInst: *Value, TailCallKind: TailCallKind) void;
|
||||
|
||||
pub const addCallSiteAttribute = LLVMAddCallSiteAttribute;
|
||||
extern fn LLVMAddCallSiteAttribute(C: *Value, Idx: AttributeIndex, A: *Attribute) void;
|
||||
|
||||
pub const fnSetSubprogram = ZigLLVMFnSetSubprogram;
|
||||
extern fn ZigLLVMFnSetSubprogram(f: *Value, subprogram: *DISubprogram) void;
|
||||
|
||||
@ -531,7 +540,7 @@ pub const Module = opaque {
|
||||
pub const createDIBuilder = ZigLLVMCreateDIBuilder;
|
||||
extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) *DIBuilder;
|
||||
|
||||
pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2;
|
||||
pub const setModuleInlineAsm = LLVMSetModuleInlineAsm2;
|
||||
extern fn LLVMSetModuleInlineAsm2(M: *Module, Asm: [*]const u8, Len: usize) void;
|
||||
|
||||
pub const printModuleToFile = LLVMPrintModuleToFile;
|
||||
@ -642,7 +651,17 @@ pub const Builder = opaque {
|
||||
Name: [*:0]const u8,
|
||||
) *Value;
|
||||
|
||||
pub const buildCall = ZigLLVMBuildCall;
|
||||
pub const buildCall = LLVMBuildCall2;
|
||||
extern fn LLVMBuildCall2(
|
||||
*Builder,
|
||||
*Type,
|
||||
Fn: *Value,
|
||||
Args: [*]const *Value,
|
||||
NumArgs: c_uint,
|
||||
Name: [*:0]const u8,
|
||||
) *Value;
|
||||
|
||||
pub const buildCallOld = ZigLLVMBuildCall;
|
||||
extern fn ZigLLVMBuildCall(
|
||||
*Builder,
|
||||
*Type,
|
||||
@ -1605,6 +1624,13 @@ pub const CallAttr = enum(c_int) {
|
||||
AlwaysInline,
|
||||
};
|
||||
|
||||
pub const TailCallKind = enum(c_uint) {
|
||||
None,
|
||||
Tail,
|
||||
MustTail,
|
||||
NoTail,
|
||||
};
|
||||
|
||||
pub const DLLStorageClass = enum(c_uint) {
|
||||
Default,
|
||||
DLLImport,
|
||||
|
||||
@ -453,6 +453,10 @@ LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
ZIG_EXTERN_C void ZigLLVMSetTailCallKind(LLVMValueRef Call, CallInst::TailCallKind TailCallKind) {
|
||||
unwrap<CallInst>(Call)->setTailCallKind(TailCallKind);
|
||||
}
|
||||
|
||||
void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) {
|
||||
if (isa<Function>(unwrap(Val))) {
|
||||
unwrap<Function>(Val)->addAttributeAtIndex(Idx, unwrap(A));
|
||||
@ -461,7 +465,6 @@ void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
|
||||
LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile)
|
||||
{
|
||||
@ -1116,11 +1119,6 @@ void ZigLLVMAddFunctionAttr(LLVMValueRef fn_ref, const char *attr_name, const ch
|
||||
func->addFnAttr(attr_name, attr_value);
|
||||
}
|
||||
|
||||
void ZigLLVMAddFunctionAttrCold(LLVMValueRef fn_ref) {
|
||||
Function *func = unwrap<Function>(fn_ref);
|
||||
func->addFnAttr(Attribute::Cold);
|
||||
}
|
||||
|
||||
void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv) {
|
||||
cl::ParseCommandLineOptions(argc, argv);
|
||||
}
|
||||
|
||||
@ -4,5 +4,6 @@ const Cases = @import("src/Cases.zig");
|
||||
pub fn addCases(cases: *Cases) !void {
|
||||
try @import("compile_errors.zig").addCases(cases);
|
||||
try @import("cbe.zig").addCases(cases);
|
||||
try @import("llvm_targets.zig").addCases(cases);
|
||||
try @import("nvptx.zig").addCases(cases);
|
||||
}
|
||||
|
||||
117
test/llvm_targets.zig
Normal file
117
test/llvm_targets.zig
Normal file
@ -0,0 +1,117 @@
|
||||
const std = @import("std");
|
||||
const Cases = @import("src/Cases.zig");
|
||||
|
||||
const targets = [_]std.zig.CrossTarget{
|
||||
.{ .cpu_arch = .aarch64, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64, .os_tag = .macos, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64, .os_tag = .uefi, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .gnu },
|
||||
.{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .msvc },
|
||||
.{ .cpu_arch = .aarch64_be, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64_be, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64_32, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .aarch64_32, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .amdgcn, .os_tag = .amdhsa, .abi = .none },
|
||||
.{ .cpu_arch = .amdgcn, .os_tag = .amdpal, .abi = .none },
|
||||
.{ .cpu_arch = .amdgcn, .os_tag = .linux, .abi = .none },
|
||||
//.{ .cpu_arch = .amdgcn, .os_tag = .mesa3d, .abi = .none },
|
||||
.{ .cpu_arch = .arm, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .arm, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .arm, .os_tag = .uefi, .abi = .none },
|
||||
.{ .cpu_arch = .armeb, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .avr, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .avr, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .avr, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .bpfel, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .bpfel, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .bpfeb, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .bpfeb, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .hexagon, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .mips, .os_tag = .linux, .abi = .gnueabihf },
|
||||
.{ .cpu_arch = .mips, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .mips, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .gnueabihf },
|
||||
.{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .msp430, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .msp430, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .msp430, .os_tag = .linux, .abi = .none },
|
||||
//.{ .cpu_arch = .nvptx, .os_tag = .cuda, .abi = .none },
|
||||
//.{ .cpu_arch = .nvptx64, .os_tag = .cuda, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .gnueabihf },
|
||||
.{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .powerpcle, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .powerpcle, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .powerpcle, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .powerpcle, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .powerpcle, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc64, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc64, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc64le, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc64le, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .none },
|
||||
//.{ .cpu_arch = .r600, .os_tag = .mesa3d, .abi = .none },
|
||||
.{ .cpu_arch = .riscv32, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .riscv32, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .riscv64, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .musl },
|
||||
.{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .s390x, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .s390x, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .sparc, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .sparc, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .sparc, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .sparcel, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .sparcel, .os_tag = .linux, .abi = .gnu },
|
||||
.{ .cpu_arch = .sparc64, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .sparc64, .os_tag = .linux, .abi = .gnu },
|
||||
//.{ .cpu_arch = .spirv32, .os_tag = .opencl, .abi = .none },
|
||||
//.{ .cpu_arch = .spirv32, .os_tag = .glsl450, .abi = .none },
|
||||
//.{ .cpu_arch = .spirv32, .os_tag = .vulkan, .abi = .none },
|
||||
//.{ .cpu_arch = .spirv64, .os_tag = .opencl, .abi = .none },
|
||||
//.{ .cpu_arch = .spirv64, .os_tag = .glsl450, .abi = .none },
|
||||
//.{ .cpu_arch = .spirv64, .os_tag = .vulkan, .abi = .none },
|
||||
.{ .cpu_arch = .thumb, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .thumbeb, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .ve, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .wasm32, .os_tag = .emscripten, .abi = .none },
|
||||
.{ .cpu_arch = .wasm32, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .wasm32, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .wasm32, .os_tag = .wasi, .abi = .none },
|
||||
.{ .cpu_arch = .wasm64, .os_tag = .emscripten, .abi = .none },
|
||||
.{ .cpu_arch = .wasm64, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .wasm64, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .wasm64, .os_tag = .wasi, .abi = .none },
|
||||
.{ .cpu_arch = .x86, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .x86, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .x86, .os_tag = .uefi, .abi = .none },
|
||||
.{ .cpu_arch = .x86, .os_tag = .windows, .abi = .gnu },
|
||||
.{ .cpu_arch = .x86, .os_tag = .windows, .abi = .msvc },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .freebsd, .abi = .none },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .freestanding, .abi = .none },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .none },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .macos, .abi = .none },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .uefi, .abi = .none },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu },
|
||||
.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .msvc },
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *Cases) !void {
|
||||
for (targets) |target| {
|
||||
var case = ctx.noEmitUsingLlvmBackend("llvm_targets", target);
|
||||
case.addCompile("");
|
||||
}
|
||||
}
|
||||
@ -76,6 +76,7 @@ pub const Case = struct {
|
||||
output_mode: std.builtin.OutputMode,
|
||||
optimize_mode: std.builtin.Mode = .Debug,
|
||||
updates: std.ArrayList(Update),
|
||||
emit_bin: bool = true,
|
||||
emit_h: bool = false,
|
||||
is_test: bool = false,
|
||||
expect_exact: bool = false,
|
||||
@ -176,6 +177,19 @@ pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Cas
|
||||
return &ctx.cases.items[ctx.cases.items.len - 1];
|
||||
}
|
||||
|
||||
pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
|
||||
ctx.cases.append(Case{
|
||||
.name = name,
|
||||
.target = target,
|
||||
.updates = std.ArrayList(Update).init(ctx.cases.allocator),
|
||||
.output_mode = .Obj,
|
||||
.emit_bin = false,
|
||||
.deps = std.ArrayList(DepModule).init(ctx.arena),
|
||||
.backend = .llvm,
|
||||
}) catch @panic("out of memory");
|
||||
return &ctx.cases.items[ctx.cases.items.len - 1];
|
||||
}
|
||||
|
||||
/// Adds a test case that uses the LLVM backend to emit an executable.
|
||||
/// Currently this implies linking libc, because only then we can generate a testable executable.
|
||||
pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case {
|
||||
@ -537,6 +551,8 @@ pub fn lowerToBuildSteps(
|
||||
}),
|
||||
};
|
||||
|
||||
artifact.emit_bin = if (case.emit_bin) .default else .no_emit;
|
||||
|
||||
if (case.link_libc) artifact.linkLibC();
|
||||
|
||||
switch (case.backend) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user