Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WebAssembly] Define call-indirect-overlong and bulk-memory-opt features #117087

Merged
merged 15 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5092,6 +5092,10 @@ def matomics : Flag<["-"], "matomics">, Group<m_wasm_Features_Group>;
def mno_atomics : Flag<["-"], "mno-atomics">, Group<m_wasm_Features_Group>;
def mbulk_memory : Flag<["-"], "mbulk-memory">, Group<m_wasm_Features_Group>;
def mno_bulk_memory : Flag<["-"], "mno-bulk-memory">, Group<m_wasm_Features_Group>;
def mbulk_memory_opt : Flag<["-"], "mbulk-memory-opt">, Group<m_wasm_Features_Group>;
def mno_bulk_memory_opt : Flag<["-"], "mno-bulk-memory-opt">, Group<m_wasm_Features_Group>;
def mcall_indirect_overlong : Flag<["-"], "mcall-indirect-overlong">, Group<m_wasm_Features_Group>;
def mno_call_indirect_overlong : Flag<["-"], "mno-call-indirect-overlong">, Group<m_wasm_Features_Group>;
def mexception_handing : Flag<["-"], "mexception-handling">, Group<m_wasm_Features_Group>;
def mno_exception_handing : Flag<["-"], "mno-exception-handling">, Group<m_wasm_Features_Group>;
def mextended_const : Flag<["-"], "mextended-const">, Group<m_wasm_Features_Group>;
Expand Down
34 changes: 34 additions & 0 deletions clang/lib/Basic/Targets/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
return llvm::StringSwitch<bool>(Feature)
.Case("atomics", HasAtomics)
.Case("bulk-memory", HasBulkMemory)
.Case("bulk-memory-opt", HasBulkMemoryOpt)
.Case("call-indirect-overlong", HasCallIndirectOverlong)
.Case("exception-handling", HasExceptionHandling)
.Case("extended-const", HasExtendedConst)
.Case("fp16", HasFP16)
Expand Down Expand Up @@ -79,6 +81,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__wasm_atomics__");
if (HasBulkMemory)
Builder.defineMacro("__wasm_bulk_memory__");
if (HasBulkMemoryOpt)
Builder.defineMacro("__wasm_bulk_memory_opt__");
Comment on lines +84 to +85
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this for call-indirect-overlong?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't think of a reason C/C++ code would need to know about call-indirect overlongs. I'm not opposed to adding it if we think there might be a reason though.

if (HasExceptionHandling)
Builder.defineMacro("__wasm_exception_handling__");
if (HasExtendedConst)
Expand Down Expand Up @@ -155,6 +159,8 @@ bool WebAssemblyTargetInfo::initFeatureMap(
const std::vector<std::string> &FeaturesVec) const {
auto addGenericFeatures = [&]() {
Features["bulk-memory"] = true;
Features["bulk-memory-opt"] = true;
Features["call-indirect-overlong"] = true;
Features["multivalue"] = true;
Features["mutable-globals"] = true;
Features["nontrapping-fptoint"] = true;
Expand Down Expand Up @@ -200,6 +206,22 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
HasBulkMemory = false;
continue;
}
if (Feature == "+bulk-memory-opt") {
HasBulkMemoryOpt = true;
continue;
}
if (Feature == "-bulk-memory-opt") {
HasBulkMemoryOpt = false;
continue;
}
if (Feature == "+call-indirect-overlong") {
HasCallIndirectOverlong = true;
continue;
}
if (Feature == "-call-indirect-overlong") {
HasCallIndirectOverlong = false;
continue;
}
if (Feature == "+exception-handling") {
HasExceptionHandling = true;
continue;
Expand Down Expand Up @@ -310,6 +332,18 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
<< Feature << "-target-feature";
return false;
}

// bulk-memory-opt is a subset of bulk-memory.
if (HasBulkMemory) {
HasBulkMemoryOpt = true;
}

// The reference-types feature included the change to `call_indirect`
// encodings to support overlong immediates.
if (HasReferenceTypes) {
HasCallIndirectOverlong = true;
}

return true;
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/WebAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {

bool HasAtomics = false;
bool HasBulkMemory = false;
bool HasBulkMemoryOpt = false;
bool HasCallIndirectOverlong = false;
bool HasExceptionHandling = false;
bool HasExtendedConst = false;
bool HasFP16 = false;
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/compress-relocs.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: llvm-mc -mattr=+reference-types -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/call-indirect.s -o %t2.o
; RUN: llvm-mc -mattr=+call-indirect-overlong -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/call-indirect.s -o %t2.o
; RUN: wasm-ld --export-dynamic -o %t.wasm %t2.o %t.o
; RUN: obj2yaml %t.wasm | FileCheck %s
; RUN: wasm-ld --export-dynamic -O2 -o %t-opt.wasm %t2.o %t.o
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/import-table-explicit.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# RUN: llvm-mc -mattr=+reference-types -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
# RUN: llvm-mc -mattr=+call-indirect-overlong -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
# RUN: wasm-ld --import-table -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s

Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/invalid-mvp-table-use.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
#
# If any table is defined or declared besides the __indirect_function_table,
# the compilation unit should be compiled with -mattr=+reference-types,
# the compilation unit should be compiled with -mattr=+call-indirect-overlong,
# causing symbol table entries to be emitted for all tables.
# RUN: not wasm-ld --no-entry %t.o -o %t.wasm 2>&1 | FileCheck -check-prefix=CHECK-ERR %s

Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/lto/Inputs/libcall-archive.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ define void @memcpy() #0 {
ret void
}

attributes #0 = { "target-features"="-bulk-memory" }
attributes #0 = { "target-features"="-bulk-memory,-bulk-memory-opt" }
2 changes: 1 addition & 1 deletion lld/test/wasm/lto/libcall-archive.ll
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ entry:

declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)

attributes #0 = { "target-features"="-bulk-memory" }
attributes #0 = { "target-features"="-bulk-memory,-bulk-memory-opt" }

; CHECK: - Type: CUSTOM
; CHECK-NEXT: Name: name
Expand Down
4 changes: 2 additions & 2 deletions lld/test/wasm/lto/stub-library-libcall.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t_main.o %t/main.s
# RUN: llvm-as %S/Inputs/foo.ll -o %t_foo.o
# RUN: llvm-as %S/Inputs/libcall.ll -o %t_libcall.o
# RUN: wasm-ld -mllvm -mattr=-bulk-memory %t_main.o %t_libcall.o %t_foo.o %p/Inputs/stub.so -o %t.wasm
# RUN: wasm-ld -mllvm -mattr=-bulk-memory,-bulk-memory-opt %t_main.o %t_libcall.o %t_foo.o %p/Inputs/stub.so -o %t.wasm
# RUN: obj2yaml %t.wasm | FileCheck %s

# The function `func_with_libcall` will generate an undefined reference to
Expand All @@ -12,7 +12,7 @@
# If %t_foo.o is not included in the link we get an undefined symbol reported
# to the dependency of memcpy on the foo export:

# RUN: not wasm-ld -mllvm -mattr=-bulk-memory %t_main.o %t_libcall.o %p/Inputs/stub.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING %s
# RUN: not wasm-ld -mllvm -mattr=-bulk-memory,-bulk-memory-opt %t_main.o %t_libcall.o %p/Inputs/stub.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING %s
# MISSING: stub.so: undefined symbol: foo. Required by memcpy

#--- main.s
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/multi-table.s
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ call_indirect_explicit_tables:
call_indirect table_b, () -> ()
end_function

# RT-MVP: wasm-ld: error: object file not built with 'reference-types' feature conflicts with import of table table_a by file
# RT-MVP: wasm-ld: error: object file not built with 'reference-types' or 'call-indirect-overlong' feature conflicts with import of table table_a by file

# CHECK: --- !WASM
# CHECK-NEXT: FileHeader:
Expand Down
21 changes: 11 additions & 10 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,14 @@ static void setRelocs(const std::vector<T *> &chunks,
}
}

// An object file can have two approaches to tables. With the reference-types
// feature enabled, input files that define or use tables declare the tables
// using symbols, and record each use with a relocation. This way when the
// linker combines inputs, it can collate the tables used by the inputs,
// assigning them distinct table numbers, and renumber all the uses as
// appropriate. At the same time, the linker has special logic to build the
// indirect function table if it is needed.
// An object file can have two approaches to tables. With the
// reference-types feature or call-indirect-overlong feature enabled
// (explicitly, or implied by the reference-types feature), input files that
// define or use tables declare the tables using symbols, and record each use
// with a relocation. This way when the linker combines inputs, it can collate
// the tables used by the inputs, assigning them distinct table numbers, and
// renumber all the uses as appropriate. At the same time, the linker has
// special logic to build the indirect function table if it is needed.
//
// However, MVP object files (those that target WebAssembly 1.0, the "minimum
// viable product" version of WebAssembly) neither write table symbols nor
Expand All @@ -284,9 +285,9 @@ void ObjFile::addLegacyIndirectFunctionTableIfNeeded(
return;

// It's possible for an input to define tables and also use the indirect
// function table, but forget to compile with -mattr=+reference-types.
// For these newer files, we require symbols for all tables, and
// relocations for all of their uses.
// function table, but forget to compile with -mattr=+call-indirect-overlong
// or -mattr=+reference-types. For these newer files, we require symbols for
// all tables, and relocations for all of their uses.
if (tableSymbolCount != 0) {
error(toString(this) +
": expected one symbol table entry for each of the " +
Expand Down
5 changes: 3 additions & 2 deletions lld/wasm/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,9 @@ void TableSection::addTable(InputTable *table) {
// to assign table number 0 to the indirect function table.
for (const auto *culprit : out.importSec->importedSymbols) {
if (isa<UndefinedTable>(culprit)) {
error("object file not built with 'reference-types' feature "
"conflicts with import of table " +
error("object file not built with 'reference-types' or "
"'call-indirect-overlong' feature conflicts with import of "
"table " +
culprit->getName() + " by file " +
toString(culprit->getFile()));
return;
Expand Down
28 changes: 21 additions & 7 deletions llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,18 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
: MCTargetAsmParser(Options, STI, MII), Parser(Parser),
Lexer(Parser.getLexer()), Is64(STI.getTargetTriple().isArch64Bit()),
TC(Parser, MII, Is64), SkipTypeCheck(Options.MCNoTypeCheck) {
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
FeatureBitset FBS = ComputeAvailableFeatures(STI.getFeatureBits());

// bulk-memory implies bulk-memory-opt
if (FBS.test(WebAssembly::FeatureBulkMemory)) {
FBS.set(WebAssembly::FeatureBulkMemoryOpt);
}
// reference-types implies call-indirect-overlong
if (FBS.test(WebAssembly::FeatureReferenceTypes)) {
FBS.set(WebAssembly::FeatureCallIndirectOverlong);
}

setAvailableFeatures(FBS);
// Don't type check if this is inline asm, since that is a naked sequence of
// instructions without a function/locals decl.
auto &SM = Parser.getSourceManager();
Expand All @@ -291,7 +302,8 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {

DefaultFunctionTable = getOrCreateFunctionTableSymbol(
getContext(), "__indirect_function_table", Is64);
if (!STI->checkFeatures("+reference-types"))
if (!STI->checkFeatures("+call-indirect-overlong") &&
!STI->checkFeatures("+reference-types"))
DefaultFunctionTable->setOmitFromLinkingSection();
}

Expand Down Expand Up @@ -531,11 +543,13 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
}

bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) {
if (STI->checkFeatures("+reference-types")) {
// If the reference-types feature is enabled, there is an explicit table
// operand. To allow the same assembly to be compiled with or without
// reference types, we allow the operand to be omitted, in which case we
// default to __indirect_function_table.
if (STI->checkFeatures("+call-indirect-overlong") ||
STI->checkFeatures("+reference-types")) {
// If the call-indirect-overlong feature is enabled, or implied by the
// reference-types feature, there is an explicit table operand. To allow
// the same assembly to be compiled with or without
// call-indirect-overlong, we allow the operand to be omitted, in which
// case we default to __indirect_function_table.
auto &Tok = Lexer.getTok();
if (Tok.is(AsmToken::Identifier)) {
auto *Sym =
Expand Down
17 changes: 13 additions & 4 deletions llvm/lib/Target/WebAssembly/WebAssembly.td
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def FeatureBulkMemory :
SubtargetFeature<"bulk-memory", "HasBulkMemory", "true",
"Enable bulk memory operations">;

def FeatureBulkMemoryOpt :
SubtargetFeature<"bulk-memory-opt", "HasBulkMemoryOpt", "true",
"Enable bulk memory optimization operations">;

def FeatureCallIndirectOverlong :
SubtargetFeature<"call-indirect-overlong", "HasCallIndirectOverlong", "true",
"Enable overlong encoding for call_indirect immediates">;

def FeatureExceptionHandling :
SubtargetFeature<"exception-handling", "HasExceptionHandling", "true",
"Enable Wasm exception handling">;
Expand Down Expand Up @@ -114,15 +122,16 @@ def : ProcessorModel<"mvp", NoSchedModel, []>;
// consideration given to available support in relevant engines and tools, and
// the importance of the features.
def : ProcessorModel<"generic", NoSchedModel,
[FeatureBulkMemory, FeatureMultivalue,
[FeatureBulkMemory, FeatureBulkMemoryOpt,
FeatureCallIndirectOverlong, FeatureMultivalue,
FeatureMutableGlobals, FeatureNontrappingFPToInt,
FeatureReferenceTypes, FeatureSignExt]>;

// Latest and greatest experimental version of WebAssembly. Bugs included!
def : ProcessorModel<"bleeding-edge", NoSchedModel,
[FeatureAtomics, FeatureBulkMemory,
FeatureExceptionHandling, FeatureExtendedConst,
FeatureFP16, FeatureMultiMemory,
[FeatureAtomics, FeatureBulkMemory, FeatureBulkMemoryOpt,
FeatureCallIndirectOverlong, FeatureExceptionHandling,
FeatureExtendedConst, FeatureFP16, FeatureMultiMemory,
FeatureMultivalue, FeatureMutableGlobals,
FeatureNontrappingFPToInt, FeatureRelaxedSIMD,
FeatureReferenceTypes, FeatureSIMD128, FeatureSignExt,
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
// The table into which this call_indirect indexes.
MCSymbolWasm *Table = WebAssembly::getOrCreateFunctionTableSymbol(
MF->getContext(), Subtarget);
if (Subtarget->hasReferenceTypes()) {
if (Subtarget->hasCallIndirectOverlong()) {
MIB.addSym(Table);
} else {
// Otherwise for the MVP there is at most one table whose number is 0, but
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB,
MF.getContext(), Subtarget)
: WebAssembly::getOrCreateFunctionTableSymbol(
MF.getContext(), Subtarget);
if (Subtarget->hasReferenceTypes()) {
if (Subtarget->hasCallIndirectOverlong()) {
MIB.addSym(Table);
} else {
// For the MVP there is at most one table whose number is 0, but we can't
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
///
//===----------------------------------------------------------------------===//

// Instruction requiring HasBulkMemory and the bulk memory prefix byte
// Instruction requiring HasBulkMemoryOpt and the bulk memory prefix byte
multiclass BULK_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
list<dag> pattern_r, string asmstr_r = "",
string asmstr_s = "", bits<32> simdop = -1> {
defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
!or(0xfc00, !and(0xff, simdop))>,
Requires<[HasBulkMemory]>;
Requires<[HasBulkMemoryOpt]>;
}

// Bespoke types and nodes for bulk memory ops
Expand Down Expand Up @@ -89,14 +89,14 @@ defm CPY_A#B : I<(outs), (ins i32imm_op:$src_idx, i32imm_op:$dst_idx,
rc:$dst, rc:$src, rc:$len
)],
"", "", 0>,
Requires<[HasBulkMemory]>;
Requires<[HasBulkMemoryOpt]>;

let usesCustomInserter = 1, isCodeGenOnly = 1, mayStore = 1 in
defm SET_A#B : I<(outs), (ins i32imm_op:$idx, rc:$dst, I32:$value, rc:$size),
(outs), (ins i32imm_op:$idx),
[(wasm_memset (i32 imm:$idx), rc:$dst, I32:$value, rc:$size)],
"", "", 0>,
Requires<[HasBulkMemory]>;
Requires<[HasBulkMemoryOpt]>;

}

Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ def HasBulkMemory :
Predicate<"Subtarget->hasBulkMemory()">,
AssemblerPredicate<(all_of FeatureBulkMemory), "bulk-memory">;

def HasBulkMemoryOpt :
Predicate<"Subtarget->hasBulkMemoryOpt()">,
AssemblerPredicate<(all_of FeatureBulkMemoryOpt), "bulk-memory-opt">;

def HasCallIndirectOverlong :
Predicate<"Subtarget->hasCallIndirectOverlong()">,
AssemblerPredicate<(all_of FeatureCallIndirectOverlong), "call-indirect-overlong">;

def HasExceptionHandling :
Predicate<"Subtarget->hasExceptionHandling()">,
AssemblerPredicate<(all_of FeatureExceptionHandling), "exception-handling">;
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemcpy(
SDValue Size, Align Alignment, bool IsVolatile, bool AlwaysInline,
MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const {
auto &ST = DAG.getMachineFunction().getSubtarget<WebAssemblySubtarget>();
if (!ST.hasBulkMemory())
if (!ST.hasBulkMemoryOpt())
return SDValue();

SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32);
Expand Down Expand Up @@ -51,7 +51,7 @@ SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemset(
SDValue Size, Align Alignment, bool IsVolatile, bool AlwaysInline,
MachinePointerInfo DstPtrInfo) const {
auto &ST = DAG.getMachineFunction().getSubtarget<WebAssemblySubtarget>();
if (!ST.hasBulkMemory())
if (!ST.hasBulkMemoryOpt())
return SDValue();

SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32);
Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU,
CPU = "generic";

ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);

FeatureBitset Bits = getFeatureBits();

// bulk-memory implies bulk-memory-opt
if (HasBulkMemory) {
HasBulkMemoryOpt = true;
Bits.set(WebAssembly::FeatureBulkMemoryOpt);
}

// reference-types implies call-indirect-overlong
if (HasReferenceTypes) {
HasCallIndirectOverlong = true;
Bits.set(WebAssembly::FeatureCallIndirectOverlong);
}

// In case we changed any bits, update `MCSubtargetInfo`'s `FeatureBitset`.
setFeatureBits(Bits);

return *this;
}

Expand Down
Loading
Loading