mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Add builtins to generate instructions for tail-allocated arrays in SIL.
Those builtins are: allocWithTailElems_<n>, getTailAddr and projectTailElems Also rename the "gep" builtin, which indexes raw bytes, to "gepRaw" and add a new "gep" builtin to index in a typed array.
This commit is contained in:
@@ -252,9 +252,23 @@ BUILTIN_SIL_OPERATION(ReinterpretCast, "reinterpretCast", Special)
|
||||
/// valid within the scope of the statement for logical lvalues.
|
||||
BUILTIN_SIL_OPERATION(AddressOf, "addressof", Special)
|
||||
|
||||
/// GetElementPtr has type (Builtin.RawPointer, T) -> Builtin.RawPointer
|
||||
/// GepRaw(Builtin.RawPointer, Builtin.Word) -> Builtin.RawPointer
|
||||
///
|
||||
/// Adds index bytes to a base pointer.
|
||||
BUILTIN_SIL_OPERATION(GepRaw, "gepRaw", Integer)
|
||||
|
||||
/// Gep (Builtin.RawPointer, Builtin.Word, T.Type) -> Builtin.RawPointer
|
||||
///
|
||||
/// Like the GepRaw-bultin, but multiplies the index by stride-of type 'T'.
|
||||
BUILTIN_SIL_OPERATION(Gep, "gep", Integer)
|
||||
|
||||
/// getTailAddr(Builtin.RawPointer,
|
||||
/// Builtin.Word, T.Type, E.Type) -> Builtin.RawPointer
|
||||
///
|
||||
/// Like the Gep-builtin, but rounds up the resulting address to a tail-
|
||||
/// allocated element type 'E'.
|
||||
BUILTIN_SIL_OPERATION(GetTailAddr, "getTailAddr", Integer)
|
||||
|
||||
/// condfail(Int1) -> ()
|
||||
/// Triggers a runtime failure if the condition is true.
|
||||
BUILTIN_SIL_OPERATION(CondFail, "condfail", Special)
|
||||
@@ -314,6 +328,18 @@ BUILTIN_SIL_OPERATION(IsUniqueOrPinned_native, "isUniqueOrPinned_native",
|
||||
/// bindMemory : <T> (Builtin.RawPointer, Builtin.Word, T.Type) -> ()
|
||||
BUILTIN_SIL_OPERATION(BindMemory, "bindMemory", Special)
|
||||
|
||||
/// allocWithTailElems_<n>(C.Type,
|
||||
/// Builtin.Word, E1.Type, ... , Builtin.Word, En.Type) -> C\
|
||||
///
|
||||
/// The integer suffix <n> specifies the number of tail-allocated arrays.
|
||||
/// Each tail-allocated array adds a counter and an element meta-type parameter.
|
||||
BUILTIN_SIL_OPERATION(AllocWithTailElems, "allocWithTailElems", Special)
|
||||
|
||||
/// projectTailElems : <C,E> (C) -> Builtin.RawPointer
|
||||
///
|
||||
/// Projects the first tail-allocated element of type E from a class C.
|
||||
BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special)
|
||||
|
||||
#undef BUILTIN_SIL_OPERATION
|
||||
|
||||
// BUILTIN_RUNTIME_CALL - A call into a runtime function.
|
||||
|
||||
@@ -250,7 +250,7 @@ getBuiltinGenericFunction(Identifier Id,
|
||||
}
|
||||
|
||||
/// Build a getelementptr operation declaration.
|
||||
static ValueDecl *getGepOperation(Identifier Id, Type ArgType) {
|
||||
static ValueDecl *getGepRawOperation(Identifier Id, Type ArgType) {
|
||||
auto &Context = ArgType->getASTContext();
|
||||
|
||||
// This is always "(i8*, IntTy) -> i8*"
|
||||
@@ -427,6 +427,11 @@ static ValueDecl *getCastOperation(ASTContext &Context, Identifier Id,
|
||||
static const char * const GenericParamNames[] = {
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z"
|
||||
};
|
||||
|
||||
static std::pair<ArchetypeType*, GenericTypeParamDecl*>
|
||||
@@ -680,6 +685,53 @@ static ValueDecl *getBindMemoryOperation(ASTContext &Context, Identifier Id) {
|
||||
return builder.build(Id);
|
||||
}
|
||||
|
||||
static ValueDecl *getAllocWithTailElemsOperation(ASTContext &Context,
|
||||
Identifier Id,
|
||||
int NumTailTypes) {
|
||||
if (NumTailTypes < 1 ||
|
||||
1 + NumTailTypes > (int)llvm::array_lengthof(GenericParamNames))
|
||||
return nullptr;
|
||||
GenericSignatureBuilder builder(Context, 1 + NumTailTypes);
|
||||
builder.addParameter(makeMetatype(makeGenericParam(0)));
|
||||
for (int Idx = 0; Idx < NumTailTypes; ++Idx) {
|
||||
builder.addParameter(makeConcrete(BuiltinIntegerType::getWordType(Context)));
|
||||
builder.addParameter(makeMetatype(makeGenericParam(Idx + 1)));
|
||||
}
|
||||
builder.setResult(makeGenericParam(0));
|
||||
return builder.build(Id);
|
||||
}
|
||||
|
||||
static ValueDecl *getProjectTailElemsOperation(ASTContext &Context,
|
||||
Identifier Id) {
|
||||
GenericSignatureBuilder builder(Context, 2);
|
||||
builder.addParameter(makeGenericParam(0));
|
||||
builder.addParameter(makeMetatype(makeGenericParam(1)));
|
||||
builder.setResult(makeConcrete(Context.TheRawPointerType));
|
||||
return builder.build(Id);
|
||||
}
|
||||
|
||||
/// Build a getelementptr operation declaration.
|
||||
static ValueDecl *getGepOperation(ASTContext &Context, Identifier Id,
|
||||
Type ArgType) {
|
||||
GenericSignatureBuilder builder(Context, 1);
|
||||
builder.addParameter(makeConcrete(Context.TheRawPointerType));
|
||||
builder.addParameter(makeConcrete(ArgType));
|
||||
builder.addParameter(makeMetatype(makeGenericParam(0)));
|
||||
builder.setResult(makeConcrete(Context.TheRawPointerType));
|
||||
return builder.build(Id);
|
||||
}
|
||||
|
||||
static ValueDecl *getGetTailAddrOperation(ASTContext &Context, Identifier Id,
|
||||
Type ArgType) {
|
||||
GenericSignatureBuilder builder(Context, 2);
|
||||
builder.addParameter(makeConcrete(Context.TheRawPointerType));
|
||||
builder.addParameter(makeConcrete(ArgType));
|
||||
builder.addParameter(makeMetatype(makeGenericParam(0)));
|
||||
builder.addParameter(makeMetatype(makeGenericParam(1)));
|
||||
builder.setResult(makeConcrete(Context.TheRawPointerType));
|
||||
return builder.build(Id);
|
||||
}
|
||||
|
||||
static ValueDecl *getSizeOrAlignOfOperation(ASTContext &Context,
|
||||
Identifier Id) {
|
||||
GenericSignatureBuilder builder(Context);
|
||||
@@ -1452,6 +1504,14 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
|
||||
return nullptr;
|
||||
return getAtomicStoreOperation(Context, Id, T);
|
||||
}
|
||||
if (OperationName.startswith("allocWithTailElems_")) {
|
||||
OperationName = OperationName.drop_front(strlen("allocWithTailElems_"));
|
||||
int NumTailTypes = 0;
|
||||
if (OperationName.getAsInteger(10, NumTailTypes))
|
||||
return nullptr;
|
||||
|
||||
return getAllocWithTailElemsOperation(Context, Id, NumTailTypes);
|
||||
}
|
||||
|
||||
BuiltinValueKind BV = llvm::StringSwitch<BuiltinValueKind>(OperationName)
|
||||
#define BUILTIN(id, name, Attrs) \
|
||||
@@ -1473,12 +1533,21 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
|
||||
case BuiltinValueKind::AtomicRMW:
|
||||
case BuiltinValueKind::AtomicLoad:
|
||||
case BuiltinValueKind::AtomicStore:
|
||||
case BuiltinValueKind::AllocWithTailElems:
|
||||
llvm_unreachable("Handled above");
|
||||
case BuiltinValueKind::None: return nullptr;
|
||||
|
||||
case BuiltinValueKind::GepRaw:
|
||||
if (Types.size() != 1) return nullptr;
|
||||
return getGepRawOperation(Id, Types[0]);
|
||||
|
||||
case BuiltinValueKind::Gep:
|
||||
if (Types.size() != 1) return nullptr;
|
||||
return getGepOperation(Id, Types[0]);
|
||||
return getGepOperation(Context, Id, Types[0]);
|
||||
|
||||
case BuiltinValueKind::GetTailAddr:
|
||||
if (Types.size() != 1) return nullptr;
|
||||
return getGetTailAddrOperation(Context, Id, Types[0]);
|
||||
|
||||
#define BUILTIN(id, name, Attrs)
|
||||
#define BUILTIN_BINARY_OPERATION(id, name, attrs, overload) case BuiltinValueKind::id:
|
||||
@@ -1556,6 +1625,10 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
|
||||
if (!Types.empty()) return nullptr;
|
||||
return getBindMemoryOperation(Context, Id);
|
||||
|
||||
case BuiltinValueKind::ProjectTailElems:
|
||||
if (!Types.empty()) return nullptr;
|
||||
return getProjectTailElemsOperation(Context, Id);
|
||||
|
||||
case BuiltinValueKind::Sizeof:
|
||||
case BuiltinValueKind::Strideof:
|
||||
case BuiltinValueKind::Alignof:
|
||||
|
||||
@@ -463,6 +463,8 @@ const BuiltinInfo &SILModule::getBuiltinInfo(Identifier ID) {
|
||||
Info.ID = BuiltinValueKind::AtomicLoad;
|
||||
else if (OperationName.startswith("atomicstore_"))
|
||||
Info.ID = BuiltinValueKind::AtomicStore;
|
||||
else if (OperationName.startswith("allocWithTailElems_"))
|
||||
Info.ID = BuiltinValueKind::AllocWithTailElems;
|
||||
else {
|
||||
// Switch through the rest of builtins.
|
||||
Info.ID = llvm::StringSwitch<BuiltinValueKind>(OperationName)
|
||||
|
||||
@@ -478,6 +478,21 @@ static ManagedValue emitBuiltinAddressOf(SILGenFunction &gen,
|
||||
return ManagedValue::forUnmanaged(result);
|
||||
}
|
||||
|
||||
/// Specialized emitter for Builtin.gepRaw.
|
||||
static ManagedValue emitBuiltinGepRaw(SILGenFunction &gen,
|
||||
SILLocation loc,
|
||||
ArrayRef<Substitution> substitutions,
|
||||
ArrayRef<ManagedValue> args,
|
||||
CanFunctionType formalApplyType,
|
||||
SGFContext C) {
|
||||
assert(args.size() == 2 && "gepRaw should be given two arguments");
|
||||
|
||||
SILValue offsetPtr = gen.B.createIndexRawPointer(loc,
|
||||
args[0].getUnmanagedValue(),
|
||||
args[1].getUnmanagedValue());
|
||||
return ManagedValue::forUnmanaged(offsetPtr);
|
||||
}
|
||||
|
||||
/// Specialized emitter for Builtin.gep.
|
||||
static ManagedValue emitBuiltinGep(SILGenFunction &gen,
|
||||
SILLocation loc,
|
||||
@@ -485,12 +500,39 @@ static ManagedValue emitBuiltinGep(SILGenFunction &gen,
|
||||
ArrayRef<ManagedValue> args,
|
||||
CanFunctionType formalApplyType,
|
||||
SGFContext C) {
|
||||
assert(args.size() == 2 && "gep should be given two arguments");
|
||||
assert(substitutions.size() == 1 && "gep should have two substitutions");
|
||||
assert(args.size() == 3 && "gep should be given three arguments");
|
||||
|
||||
SILValue offsetPtr = gen.B.createIndexRawPointer(loc,
|
||||
args[0].getUnmanagedValue(),
|
||||
args[1].getUnmanagedValue());
|
||||
return ManagedValue::forUnmanaged(offsetPtr);
|
||||
SILType ElemTy = gen.getLoweredType(substitutions[0].getReplacement());
|
||||
SILType RawPtrType = args[0].getUnmanagedValue()->getType();
|
||||
SILValue addr = gen.B.createPointerToAddress(loc, args[0].getUnmanagedValue(),
|
||||
ElemTy.getAddressType(), true);
|
||||
addr = gen.B.createIndexAddr(loc, addr, args[1].getUnmanagedValue());
|
||||
addr = gen.B.createAddressToPointer(loc, addr, RawPtrType);
|
||||
|
||||
return ManagedValue::forUnmanaged(addr);
|
||||
}
|
||||
|
||||
/// Specialized emitter for Builtin.getTailAddr.
|
||||
static ManagedValue emitBuiltinGetTailAddr(SILGenFunction &gen,
|
||||
SILLocation loc,
|
||||
ArrayRef<Substitution> substitutions,
|
||||
ArrayRef<ManagedValue> args,
|
||||
CanFunctionType formalApplyType,
|
||||
SGFContext C) {
|
||||
assert(substitutions.size() == 2 && "getTailAddr should have two substitutions");
|
||||
assert(args.size() == 4 && "gep should be given four arguments");
|
||||
|
||||
SILType ElemTy = gen.getLoweredType(substitutions[0].getReplacement());
|
||||
SILType TailTy = gen.getLoweredType(substitutions[1].getReplacement());
|
||||
SILType RawPtrType = args[0].getUnmanagedValue()->getType();
|
||||
SILValue addr = gen.B.createPointerToAddress(loc, args[0].getUnmanagedValue(),
|
||||
ElemTy.getAddressType(), true);
|
||||
addr = gen.B.createTailAddr(loc, addr, args[1].getUnmanagedValue(),
|
||||
TailTy.getAddressType());
|
||||
addr = gen.B.createAddressToPointer(loc, addr, RawPtrType);
|
||||
|
||||
return ManagedValue::forUnmanaged(addr);
|
||||
}
|
||||
|
||||
/// Specialized emitter for Builtin.condfail.
|
||||
@@ -804,6 +846,55 @@ static ManagedValue emitBuiltinBindMemory(SILGenFunction &gen,
|
||||
return ManagedValue::forUnmanaged(gen.emitEmptyTuple(loc));
|
||||
}
|
||||
|
||||
static ManagedValue emitBuiltinAllocWithTailElems(SILGenFunction &gen,
|
||||
SILLocation loc,
|
||||
ArrayRef<Substitution> subs,
|
||||
ArrayRef<ManagedValue> args,
|
||||
CanFunctionType formalApplyType,
|
||||
SGFContext C) {
|
||||
unsigned NumTailTypes = subs.size() - 1;
|
||||
assert(args.size() == NumTailTypes * 2 + 1 &&
|
||||
"wrong number of substitutions for allocWithTailElems");
|
||||
|
||||
// The substitution determines the element type for bound memory.
|
||||
SILType RefType = gen.getLoweredType(subs[0].getReplacement()->
|
||||
getCanonicalType()).getObjectType();
|
||||
|
||||
SmallVector<SILValue, 4> Counts;
|
||||
SmallVector<SILType, 4> ElemTypes;
|
||||
for (unsigned Idx = 0; Idx < NumTailTypes; ++Idx) {
|
||||
Counts.push_back(args[Idx * 2 + 1].getValue());
|
||||
ElemTypes.push_back(gen.getLoweredType(subs[Idx+1].getReplacement()->
|
||||
getCanonicalType()).getObjectType());
|
||||
}
|
||||
|
||||
SILValue result = gen.B.createAllocRef(loc, RefType, false, ElemTypes, Counts);
|
||||
|
||||
return ManagedValue::forUnmanaged(result);
|
||||
}
|
||||
|
||||
static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &gen,
|
||||
SILLocation loc,
|
||||
ArrayRef<Substitution> subs,
|
||||
ArrayRef<ManagedValue> args,
|
||||
CanFunctionType formalApplyType,
|
||||
SGFContext C) {
|
||||
assert(subs.size() == 2 &&
|
||||
"allocWithTailElems should have two substitutions");
|
||||
assert(args.size() == 2 &&
|
||||
"allocWithTailElems should have three arguments");
|
||||
|
||||
// The substitution determines the element type for bound memory.
|
||||
SILType ElemType = gen.getLoweredType(subs[1].getReplacement()->
|
||||
getCanonicalType()).getObjectType();
|
||||
|
||||
SILValue result = gen.B.createRefTailAddr(loc, args[0].getValue(),
|
||||
ElemType.getAddressType());
|
||||
SILType rawPointerType = SILType::getRawPointerType(gen.F.getASTContext());
|
||||
result = gen.B.createAddressToPointer(loc, result, rawPointerType);
|
||||
return ManagedValue::forUnmanaged(result);
|
||||
}
|
||||
|
||||
/// Specialized emitter for type traits.
|
||||
template<TypeTraitResult (TypeBase::*Trait)(),
|
||||
BuiltinValueKind Kind>
|
||||
|
||||
@@ -489,7 +489,7 @@ public func < <Pointee>(lhs: ${Self}<Pointee>, rhs: ${Self}<Pointee>) -> Bool {
|
||||
@_transparent
|
||||
public func + <Pointee>(lhs: ${Self}<Pointee>, rhs: Int) -> ${Self}<Pointee> {
|
||||
return ${Self}(Builtin.gep_Word(
|
||||
lhs._rawValue, (rhs &* MemoryLayout<Pointee>.stride)._builtinWordValue))
|
||||
lhs._rawValue, rhs._builtinWordValue, Pointee.self))
|
||||
}
|
||||
|
||||
@_transparent
|
||||
|
||||
@@ -468,7 +468,7 @@ public struct Unsafe${Mutable}RawPointer : Strideable, Hashable, _Pointer {
|
||||
|
||||
/// Returns `self + n`.
|
||||
public func advanced(by n: Int) -> ${Self} {
|
||||
return ${Self}(Builtin.gep_Word(_rawValue, n._builtinWordValue))
|
||||
return ${Self}(Builtin.gepRaw_Word(_rawValue, n._builtinWordValue))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,9 +120,9 @@ func !=(lhs: Int, rhs: Int) -> Bool {
|
||||
// CHECK: icmp ne i32
|
||||
}
|
||||
|
||||
func gep_test(_ ptr: Builtin.RawPointer, offset: Builtin.Int64)
|
||||
func gepRaw_test(_ ptr: Builtin.RawPointer, offset: Builtin.Int64)
|
||||
-> Builtin.RawPointer {
|
||||
return Builtin.gep_Int64(ptr, offset)
|
||||
return Builtin.gepRaw_Int64(ptr, offset)
|
||||
// CHECK: getelementptr inbounds i8, i8*
|
||||
}
|
||||
|
||||
|
||||
@@ -345,18 +345,62 @@ func existential_from_raw_pointer(_ p: Builtin.RawPointer) -> AnyObject {
|
||||
return Builtin.bridgeFromRawPointer(p)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins5gep64
|
||||
func gep64(_ p: Builtin.RawPointer, i: Builtin.Int64) -> Builtin.RawPointer {
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins9gep_raw64
|
||||
func gep_raw64(_ p: Builtin.RawPointer, i: Builtin.Int64) -> Builtin.RawPointer {
|
||||
// CHECK: [[GEP:%.*]] = index_raw_pointer
|
||||
// CHECK: return [[GEP]]
|
||||
return Builtin.gep_Int64(p, i)
|
||||
return Builtin.gepRaw_Int64(p, i)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins5gep32
|
||||
func gep32(_ p: Builtin.RawPointer, i: Builtin.Int32) -> Builtin.RawPointer {
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins9gep_raw32
|
||||
func gep_raw32(_ p: Builtin.RawPointer, i: Builtin.Int32) -> Builtin.RawPointer {
|
||||
// CHECK: [[GEP:%.*]] = index_raw_pointer
|
||||
// CHECK: return [[GEP]]
|
||||
return Builtin.gep_Int32(p, i)
|
||||
return Builtin.gepRaw_Int32(p, i)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins3gep
|
||||
func gep<Elem>(_ p: Builtin.RawPointer, i: Builtin.Word, e: Elem.Type) -> Builtin.RawPointer {
|
||||
// CHECK: [[P2A:%.*]] = pointer_to_address %0
|
||||
// CHECK: [[GEP:%.*]] = index_addr [[P2A]] : $*Elem, %1 : $Builtin.Word
|
||||
// CHECK: [[A2P:%.*]] = address_to_pointer [[GEP]]
|
||||
// CHECK: return [[A2P]]
|
||||
return Builtin.gep_Word(p, i, e)
|
||||
}
|
||||
|
||||
public final class Header { }
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins20allocWithTailElems_1
|
||||
func allocWithTailElems_1<T>(n: Builtin.Word, ty: T.Type) -> Header {
|
||||
// CHECK: [[M:%.*]] = metatype $@thick Header.Type
|
||||
// CHECK: [[A:%.*]] = alloc_ref [tail_elems $T * %0 : $Builtin.Word] $Header
|
||||
// CHECK: return [[A]]
|
||||
return Builtin.allocWithTailElems_1(Header.self, n, ty)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins20allocWithTailElems_3
|
||||
func allocWithTailElems_3<T1, T2, T3>(n1: Builtin.Word, ty1: T1.Type, n2: Builtin.Word, ty2: T2.Type, n3: Builtin.Word, ty3: T3.Type) -> Header {
|
||||
// CHECK: [[M:%.*]] = metatype $@thick Header.Type
|
||||
// CHECK: [[A:%.*]] = alloc_ref [tail_elems $T1 * %0 : $Builtin.Word] [tail_elems $T2 * %2 : $Builtin.Word] [tail_elems $T3 * %4 : $Builtin.Word] $Header
|
||||
// CHECK: return [[A]]
|
||||
return Builtin.allocWithTailElems_3(Header.self, n1, ty1, n2, ty2, n3, ty3)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins16projectTailElems
|
||||
func projectTailElems<T>(h: Header, ty: T.Type) -> Builtin.RawPointer {
|
||||
// CHECK: [[TA:%.*]] = ref_tail_addr %0 : $Header
|
||||
// CHECK: [[A2P:%.*]] = address_to_pointer [[TA]]
|
||||
// CHECK: return [[A2P]]
|
||||
return Builtin.projectTailElems(h, ty)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins11getTailAddr
|
||||
func getTailAddr<T1, T2>(start: Builtin.RawPointer, i: Builtin.Word, ty1: T1.Type, ty2: T2.Type) -> Builtin.RawPointer {
|
||||
// CHECK: [[P2A:%.*]] = pointer_to_address %0
|
||||
// CHECK: [[TA:%.*]] = tail_addr [[P2A]] : $*T1, %1 : $Builtin.Word, $T2
|
||||
// CHECK: [[A2P:%.*]] = address_to_pointer [[TA]]
|
||||
// CHECK: return [[A2P]]
|
||||
return Builtin.getTailAddr_Word(start, i, ty1, ty2)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_TF8builtins8condfail
|
||||
|
||||
@@ -30,5 +30,5 @@ public class C {}
|
||||
}
|
||||
|
||||
@_transparent public func gep32(p p: Builtin.RawPointer, i: Builtin.Int32) -> Builtin.RawPointer {
|
||||
return Builtin.gep_Int32(p, i)
|
||||
return Builtin.gepRaw_Int32(p, i)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user