aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CodeGen/TargetLoweringBase.cpp
AgeCommit message (Collapse)AuthorFilesLines
2020-10-12[X86][SelectionDAG] Add SADDO_CARRY and SSUBO_CARRY to support multipart ↵Craig Topper1-0/+2
signed add/sub overflow legalization. This passes existing X86 test but I'm not sure if it handles all type legalization cases it needs to. Alternative to D89200 Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D89222
2020-10-12[SVE] Make ElementCount and TypeSize use a new PolySize classDavid Sherwood1-4/+4
I have introduced a new template PolySize class, where the template parameter determines the type of quantity, i.e. for an element count this is just an unsigned value. The ElementCount class is now just a simple derivation of PolySize<unsigned>, whereas TypeSize is more complicated because it still needs to contain the uint64_t cast operator, since there are still many places in the code that rely upon this implicit cast. As such the class also still needs some of it's own operators. I've tried to minimise the amount of code in the base PolySize class, which led to a couple of changes: 1. In some places we were relying on '==' operator comparisons between ElementCounts and the scalar value 1. I didn't put this operator in the new PolySize class, and thought it was actually clearer to use the isScalar() function instead. 2. I removed the isByteSized function and replaced it with calls to isKnownMultipleOf(8). I've also renamed NextPowerOf2 to be coefficientNextPowerOf2 so that it's more consistent with coefficientDivideBy. Differential Revision: https://reviews.llvm.org/D88409
2020-10-02[SVE][CodeGen] Add new EVT/MVT getFixedSizeInBits() functionsDavid Sherwood1-1/+1
When we know that a particular type is always going to be fixed width we have so far been writing code like this: getSizeInBits().getFixedSize() Since we are doing this in quite a few places now it seems to make sense to add a new helper function that allows us to replace these calls with a single getFixedSizeInBits() call. Differential Revision: https://reviews.llvm.org/D88649
2020-09-28[SVE] Replace / operator in TypeSize/ElementCount with divideCoefficientByDavid Sherwood1-7/+6
After some recent upstream discussion we decided that it was best to avoid having the / operator for both ElementCount and TypeSize, since this could give the impression that these classes can be used in the same way as basic integer integer types. However, division for scalable types is a bit odd because we are only dividing the minimum quantity by a value, as opposed to something like: (MinSize * Vscale) / SomeValue This is why when performing division it's important the caller first establishes whether the operation makes sense, perhaps by calling isKnownMultipleOf() prior to division. The caller must now explictly call divideCoefficientBy() on the class to perform the operation. Differential Revision: https://reviews.llvm.org/D87700
2020-09-23[SVE] Make EVT::getScalarSizeInBits and others consistent with ↵David Sherwood1-3/+2
Type::getScalarSizeInBits An existing function Type::getScalarSizeInBits returns a uint64_t instead of a TypeSize class because the caller is requesting a scalar size, which cannot be scalable. This patch makes other similar functions requesting a scalar size consistent with that, thereby eliminating more than 1000 implicit TypeSize -> uint64_t casts. Differential revision: https://reviews.llvm.org/D87889
2020-09-12[SelectionDAG][X86][ARM][AArch64] Add ISD opcode for __builtin_parity. ↵Craig Topper1-0/+1
Expand it to shifts and xors. Clang emits (and (ctpop X), 1) for __builtin_parity. If ctpop isn't natively supported by the target, this leads to poor codegen due to the expansion of ctpop being more complex than what is needed for parity. This adds a DAG combine to convert the pattern to ISD::PARITY before operation legalization. Type legalization is updated to handled Expanding and Promoting this operation. If after type legalization, CTPOP is supported for this type, LegalizeDAG will turn it back into CTPOP+AND. Otherwise LegalizeDAG will emit a series of shifts and xors followed by an AND with 1. I've avoided vectors in this patch to avoid more legalization complexity for this patch. X86 previously had a custom DAG combiner for this. This is now moved to Custom lowering for the new opcode. There is a minor regression in vector-reduce-xor-bool.ll, but a follow up patch can easily fix that. Fixes PR47433 Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D87209
2020-08-28[SVE] Make ElementCount members privateDavid Sherwood1-21/+25
This patch changes ElementCount so that the Min and Scalable members are now private and can only be accessed via the get functions getKnownMinValue() and isScalable(). In addition I've added some other member functions for more commonly used operations. Hopefully this makes the class more useful and will reduce the need for calling getKnownMinValue(). Differential Revision: https://reviews.llvm.org/D86065
2020-08-27[SSP] Restore setting the visibility of __guard_local to hidden for better ↵Brad Smith1-1/+4
code generation. Patch by: Philip Guenther
2020-08-19Revert "Revert "[NFC][llvm] Make the contructors of `ElementCount` private.""Mehdi Amini1-1/+1
Was reverted because MLIR/Flang builds were broken, these APIs have been fixed in the meantime.
2020-08-19Revert "[NFC][llvm] Make the contructors of `ElementCount` private."Mehdi Amini1-1/+1
This reverts commit 264afb9e6aebc98c353644dd0700bec808501cab. (and dependent 6b742cc48 and fc53bd610f) MLIR/Flang are broken.
2020-08-19[NFC][llvm] Make the contructors of `ElementCount` private.Francesco Petrogalli1-1/+1
Differential Revision: https://reviews.llvm.org/D86120
2020-08-07[Intrinsic] Add sshl.sat/ushl.sat, saturated shift intrinsics.Bevin Hansson1-0/+2
Summary: This patch adds two intrinsics, llvm.sshl.sat and llvm.ushl.sat, which perform signed and unsigned saturating left shift, respectively. These are useful for implementing the Embedded-C fixed point support in Clang, originally discussed in http://lists.llvm.org/pipermail/llvm-dev/2018-August/125433.html and http://lists.llvm.org/pipermail/cfe-dev/2018-May/058019.html Reviewers: leonardchan, craig.topper, bjope, jdoerfert Subscribers: hiraditya, jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D83216
2020-07-31Support addrspacecast initializers with isNoopAddrSpaceCastMatt Arsenault1-0/+5
Moves isNoopAddrSpaceCast to the TargetMachine. It logically belongs with the DataLayout.
2020-07-25[Statepoints] Support lowering gc relocations to virtual registersPhilip Reames1-1/+11
(Disabled under flag for the moment) This is part of a larger project wherein we are finally integrating lowering of gc live operands with the register allocator. Today, we force spill all operands in SelectionDAG. The code to do so is distinctly non-optimal. The approach this patch is working towards is to instead lower the relocations directly into the MI form, and let the register allocator pick which ones get spilled and which stack slots they get spilled to. In terms of performance, the later part is actually more important as it avoids redundant shuffling of values between stack slots. This particular change adds ISEL support to produce the variadic def STATEPOINT form required by the above. In particular, the first N are lowered to variadic tied def/use pairs. So new statepoint looks like this: reloc1,reloc2,... = STATEPOINT ..., base1, derived1<tied-def0>, base2, derived2<tied-def1>, ... N is limited by the maximal number of tied registers machine instruction can have (15 at the moment). The current patch is restricted to handling relocations within a single basic block. Cross block relocations (e.g. invokes) are handled via the legacy mechanism. This restriction will be relaxed in future patches. Patch By: dantrushin Differential Revision: https://reviews.llvm.org/D81648
2020-06-30[Alignment][NFC] TargetLowering::allowsMemoryAccessForAlignmentGuillaume Chatelet1-7/+7
First patch of a series to adapt TargetLowering::allowsXXX functions This patch is part of a series to introduce an Alignment type. See this thread for context: http://lists.llvm.org/pipermail/llvm-dev/2019-July/133851.html See this patch for the introduction of the type: https://reviews.llvm.org/D64790 Differential Revision: https://reviews.llvm.org/D81372
2020-06-30[Alignment][NFC] TargetLowering::allowsMemoryAccessGuillaume Chatelet1-7/+9
Second patch of a series to adapt TargetLowering::allowsXXX functions This patch is part of a series to introduce an Alignment type. See this thread for context: http://lists.llvm.org/pipermail/llvm-dev/2019-July/133851.html See this patch for the introduction of the type: https://reviews.llvm.org/D64790 Differential Revision: https://reviews.llvm.org/D82785
2020-06-18[CodeGen] Fix warnings in getVectorTypeBreakdownDavid Sherwood1-6/+6
Added NextPowerOf2() routine to TypeSize and rewritten the code in getVectorTypeBreakdown to avoid warnings being generated. Differential Revision: https://reviews.llvm.org/D81578
2020-06-09Revert "[Alignment][NFC] Migrate TargetLowering::allowsMemoryAccess"Guillaume Chatelet1-9/+7
This reverts commit f21c52667ed147903015a94643b0057319189d4e.
2020-06-09[Alignment][NFC] Migrate TargetLowering::allowsMemoryAccessGuillaume Chatelet1-7/+9
Summary: Note to downstream target maintainers: this might silently change the semantics of your code if you override `TargetLowering::allowsMemoryAccess` without marking it override. This patch is part of a series to introduce an Alignment type. See this thread for context: http://lists.llvm.org/pipermail/llvm-dev/2019-July/133851.html See this patch for the introduction of the type: https://reviews.llvm.org/D64790 Reviewers: courbet Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D81379
2020-06-08[CodeGen][SVE] CopyToReg: Split scalable EVTs that are not powers of 2Sander de Smalen1-2/+34
Scalable vectors cannot use 'BUILD_VECTOR', so it is necessary to properly split and widen scalable vectors when passing them to CopyToReg/CopyFromReg. This functionality is added to TargetLoweringBase::getVectorTypeBreakdown(). This patch only adds support for 'splitting' scalable vectors that are a multiple of some legal type, e.g. <vscale x 6 x i64> -> 3 x <vscale x 2 x i64> Reviewers: efriedma, c-rhodes Reviewed By: efriedma Tags: #llvm Differential Revision: https://reviews.llvm.org/D80139
2020-06-05Reland D80640: [CodeGen][SVE] Calculate correct type legalization for ↵Sander de Smalen1-15/+19
scalable vectors. This reverts commit 9bcef270d7a319c6c0fdffc6c80984a8f0a30ecb.
2020-06-05Revert "[CodeGen][SVE] Calculate correct type legalization for scalable ↵Sander de Smalen1-19/+17
vectors." Seems to break some buildbots, reverting the patch for now. This reverts commit 164f4b9d26fdf3cd640a09b63b5ec44d033cbe8a.
2020-06-05[CodeGen][SVE] Calculate correct type legalization for scalable vectors.Sander de Smalen1-17/+19
This patch updates TargetLoweringBase::computeRegisterProperties and TargetLoweringBase::getTypeConversion to support scalable vectors, and make the right calls on how to legalise them. These changes are required to legalise both MVTs and EVTs. Reviewers: efriedma, david-arm, ctetreau Reviewed By: efriedma Tags: #llvm Differential Revision: https://reviews.llvm.org/D80640
2020-06-05Fix build breakage caused by 66a1b83bf93ec46f6d7a06c47d5981ae154f9ea0Denis Antrushin1-3/+3
2020-06-05[TargetLowering][NFC] More efficient emitPatchpoint().Denis Antrushin1-19/+15
Current implementation of emitPatchpoint() is very inefficient: for every FrameIndex operand if creates new MachineInstr with that operand expanded and all other copied as is. Since PATCHPOINT/STATEPOINT instructions may have *a lot* of FrameIndex operands, we end up creating and erasing many machine instructions. But we can do it in single pass, with only one new machine instruction generated. Reviewed By: reames Differential Revision: https://reviews.llvm.org/D81181
2020-05-26[FPEnv] Intrinsic llvm.roundevenSerge Pavlov1-0/+2
This intrinsic implements IEEE-754 operation roundToIntegralTiesToEven, and performs rounding to the nearest integer value, rounding halfway cases to even. The intrinsic represents the missed case of IEEE-754 rounding operations and now llvm provides full support of the rounding operations defined by the standard. Differential Revision: https://reviews.llvm.org/D75670
2020-05-22TargetLowering.h - remove unnecessary includes. NFC.Simon Pilgrim1-0/+1
Replace with forward declarations and move SizeOpts.h down to TargetLoweringBase.cpp
2020-05-22[TargetLowering] Move TargetLoweringBase::isJumpTableRelative() ↵Simon Pilgrim1-0/+4
implementation into TargetLoweringBase.cpp. NFC. This will help with reducing header dependencies in TargetLowering.h in a future patch.
2020-05-07[SVE][CodeGen] Fix legalisation for scalable typesKerry McLaughlin1-9/+9
Summary: This patch handles illegal scalable types when lowering IR operations, addressing several places where the value of isScalableVector() is ignored. For types such as <vscale x 8 x i32>, this means splitting the operations. In this example, we would split it into two operations of type <vscale x 4 x i32> for the low and high halves. In cases such as <vscale x 2 x i32>, the elements in the vector will be promoted. In this case they will be promoted to i64 (with a vector of type <vscale x 2 x i64>) Reviewers: sdesmalen, efriedma, huntergr Reviewed By: efriedma Subscribers: david-arm, tschuett, hiraditya, rkruppe, psnobl, cfe-commits, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D78812
2020-04-10[llvm][Codegen] Make `getVectorTypeBreakdownMVT` work with scalable types.Francesco Petrogalli1-15/+18
Reviewers: efriedma, andwar, sdesmalen Reviewed By: efriedma Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77434
2020-04-06[llvm][CodeGen] Avoid implicit cast of TypeSize to integer in `initActions`.Francesco Petrogalli1-1/+1
Reviewers: sdesmalen, efriedma Reviewed By: efriedma Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77317
2020-04-02[Alignment][NFC] Use more Align versions of various functionsGuillaume Chatelet1-2/+2
Summary: This is patch is part of a series to introduce an Alignment type. See this thread for context: http://lists.llvm.org/pipermail/llvm-dev/2019-July/133851.html See this patch for the introduction of the type: https://reviews.llvm.org/D64790 Reviewers: courbet Subscribers: MatzeB, qcolombet, arsenm, sdardis, jvesely, nhaehnle, hiraditya, jrtc27, atanasyan, kerbowa, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77291
2020-03-31[Alignment][NFC] Transitionning more getMachineMemOperand call sitesGuillaume Chatelet1-1/+1
Summary: This is patch is part of a series to introduce an Alignment type. See this thread for context: http://lists.llvm.org/pipermail/llvm-dev/2019-July/133851.html See this patch for the introduction of the type: https://reviews.llvm.org/D64790 Reviewers: courbet Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77127
2020-03-27[Alignment][NFC] MachineMemOperand::getAlign/getBaseAlignGuillaume Chatelet1-2/+2
Summary: This is patch is part of a series to introduce an Alignment type. See this thread for context: http://lists.llvm.org/pipermail/llvm-dev/2019-July/133851.html See this patch for the introduction of the type: https://reviews.llvm.org/D64790 Reviewers: courbet Subscribers: arsenm, dschuff, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, hiraditya, aheejin, kbarton, jrtc27, atanasyan, jfb, kerbowa, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D76925
2020-03-24[SelDag] Add FREEZEJuneyoung Lee1-1/+1
Summary: - Add FREEZE node to SelDag - Lower FreezeInst (in IR) to FREEZE node - Add Legalization for FREEZE node Reviewers: qcolombet, bogner, efriedma, lebedev.ri, nlopes, craig.topper, arsenm Reviewed By: lebedev.ri Subscribers: wdng, xbolva00, Petar.Avramovic, liuz, lkail, dylanmckay, hiraditya, Jim, arsenm, craig.topper, RKSimon, spatel, lebedev.ri, regehr, trentxintong, nlopes, mkuper, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D29014
2020-03-02GlobalISel: Move Localizer::shouldLocalize(..) to TargetLoweringVolkan Keles1-0/+62
Add a new target hook for shouldLocalize so that targets can customize the logic. https://reviews.llvm.org/D75207
2020-02-24[Intrinsic] Add fixed point saturating division intrinsics.Bevin Hansson1-0/+2
Summary: This patch adds intrinsics and ISelDAG nodes for signed and unsigned fixed-point division: ``` llvm.sdiv.fix.sat.* llvm.udiv.fix.sat.* ``` These intrinsics perform scaled, saturating division on two integers or vectors of integers. They are required for the implementation of the Embedded-C fixed-point arithmetic in Clang. Reviewers: bjope, leonardchan, craig.topper Subscribers: hiraditya, jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D71550
2020-02-18[NFC] Remove trailing spaceJim Lin1-1/+1
sed -Ei 's/[[:space:]]+$//' include/**/*.{def,h,td} lib/**/*.{cpp,h,td}
2020-02-01[LegalizeTypes][X86] Add a new strategy for type legalizing f16 type that ↵Craig Topper1-4/+13
softens it to i16, but promotes to f32 around arithmetic ops. This is based on this llvm-dev thread http://lists.llvm.org/pipermail/llvm-dev/2019-December/137521.html The current strategy for f16 is to promote type to float every except where the specific width is required like loads, stores, and bitcasts. This results in rounding occurring in odd places instead of immediately after arithmetic operations. This interacts in weird ways with the __fp16 type in clang which is a storage only type where arithmetic is always promoted to float. InstCombine can remove some fpext/fptruncs around such arithmetic and turn it into arithmetic on half. This wouldn't be so bad if SelectionDAG was able to put those fpext/fpround back in when it promotes. It is also not obvious how to handle to make the existing strategy work with STRICT fp. We need to use STRICT versions of the conversions which require chain operands. But if the conversions are created for a bitcast, there is no place to get an appropriate chain from. This patch implements a different strategy where conversions are emitted directly around arithmetic operations. And otherwise its passed around as an i16 including in arguments and return values. This can result in more conversions between arithmetic operations, but is closer to matching the IR the frontend generates for __fp16. And it will allow us to use the chain from constrained arithmetic nodes to link the STRICT_FP_TO_FP16/STRICT_FP16_TO_FP that will need to be added. I've set it up so that each target can opt into the new behavior. Converting all the targets myself was more than I was able to handle. Differential Revision: https://reviews.llvm.org/D73749
2020-01-27[FPEnv] Divide macro INSTRUCTION into INSTRUCTION and DAG_INSTRUCTION,Wang, Pengfei1-1/+1
and macro FUNCTION likewise. NFCI. Some functions like fmuladd don't really have a node, we should divide the declaration form those have node to avoid introducing fake nodes. Differential Revision: https://reviews.llvm.org/D72871
2020-01-16GlobalISel: Apply target MMO flags to atomicsMatt Arsenault1-0/+19
Unify MMO flag handling with SelectionDAG like with loads and stores.
2020-01-16GlobalISel: Preserve load/store metadata in IRTranslatorMatt Arsenault1-0/+37
This was dropping the invariant metadata on dead argument loads, so they weren't deleted. Atomics still need to be fixed the same way. Also, apparently store was never preserving dereferencable which should also be fixed.
2020-01-10[TargetLowering][ARM][Mips][WebAssembly] Remove the ordered FP compare from ↵Craig Topper1-5/+0
RunttimeLibcalls.def and all associated usages Summary: This always just used the same libcall as unordered, but the comparison predicate was different. This change appears to have been made when targets were given the ability to override the predicates. Before that they were hardcoded into the type legalizer. At that time we never inverted predicates and we handled ugt/ult/uge/ule compares by emitting an unordered check ORed with a ogt/olt/oge/ole checks. So only ordered needed an inverted predicate. Later ugt/ult/uge/ule were optimized to only call a single libcall and invert the compare. This patch removes the ordered entries and just uses the inverting logic that is now present. This removes some odd things in both the Mips and WebAssembly code. Reviewers: efriedma, ABataev, uweigand, cameron.mcinally, kpn Reviewed By: efriedma Subscribers: dschuff, sdardis, sbc100, arichardson, jgravelle-google, kristof.beyls, hiraditya, aheejin, sunfish, atanasyan, Petar.Avramovic, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D72536
2020-01-08[Intrinsic] Add fixed point division intrinsics.Bevin Hansson1-0/+2
Summary: This patch adds intrinsics and ISelDAG nodes for signed and unsigned fixed-point division: llvm.sdiv.fix.* llvm.udiv.fix.* These intrinsics perform scaled division on two integers or vectors of integers. They are required for the implementation of the Embedded-C fixed-point arithmetic in Clang. Patch by: ebevhan Reviewers: bjope, leonardchan, efriedma, craig.topper Reviewed By: craig.topper Subscribers: Ka-Ka, ilya, hiraditya, jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D70007
2020-01-03[DAGCombine] Initialize the default operation action for SIGN_EXTEND_INREG ↵QingShan Zhang1-0/+1
for vector type as 'expand' instead of 'legal' For now, we didn't set the default operation action for SIGN_EXTEND_INREG for vector type, which is 0 by default, that is legal. However, most target didn't have native instructions to support this opcode. It should be set as expand by default, as what we did for ANY_EXTEND_VECTOR_INREG. Differential Revision: https://reviews.llvm.org/D70000
2019-12-27Delete llvm.{sig,}{setjmp,longjmp} remnant after r136821Fangrui Song1-2/+0
Intrinsic has incorrect argument type! i32 (i32*)* @llvm.setjmp *wipes tear*
2019-11-26[Codegen][ARM] Add addressing modes from masked loads and storesDavid Green1-0/+2
MVE has a basic symmetry between it's normal loads/store operations and the masked variants. This means that masked loads and stores can use pre-inc and post-inc addressing modes, just like the standard loads and stores already do. To enable that, this patch adds all the relevant infrastructure for treating masked loads/stores addressing modes in the same way as normal loads/stores. This involves: - Adding an AddressingMode to MaskedLoadStoreSDNode, along with an extra Offset operand that is added after the PtrBase. - Extending the IndexedModeActions from 8bits to 16bits to store the legality of masked operations as well as normal ones. This array is fairly small, so doubling the size still won't make it very large. Offset masked loads can then be controlled with setIndexedMaskedLoadAction, similar to standard loads. - The same methods that combine to indexed loads, such as CombineToPostIndexedLoadStore, are adjusted to handle masked loads in the same way. - The ARM backend is then adjusted to make use of these indexed masked loads/stores. - The X86 backend is adjusted to hopefully be no functional changes. Differential Revision: https://reviews.llvm.org/D70176
2019-11-21[FPEnv] Add an option to disable strict float node mutating to an normalPengfei Wang1-0/+9
float node This patch add an option 'disable-strictnode-mutation' to prevent strict node mutating to an normal node. So we can make sure that the patch which sets strict-node as legal works correctly. Patch by Chen Liu(LiuChen3) Differential Revision: https://reviews.llvm.org/D70226
2019-11-20[FEnv] File with properties of constrained intrinsicsSerge Pavlov1-32/+3
Summary In several places we need to enumerate all constrained intrinsics or IR nodes that should be represented by them. It is easy to miss some of the cases. To make working with these intrinsics more convenient and robust, this change introduces file containing definitions of all constrained intrinsics and some of their properties. This file can be included to generate constrained intrinsics processing code. Reviewers: kpn, andrew.w.kaylor, cameron.mcinally, uweigand Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D69887
2019-11-13[TargetLowering] Increase the storage size of NumRegistersForVT to allow the ↵Craig Topper1-1/+4
type break down for v256i1 and other types to be stored correctly v256i1 on X86 without avx512 breaks down to 256 i8 values when passed between basic blocks. But the NumRegistersForVT was sized at a byte for each VT. This results in 256 being stored as 0. This patch enlarges the type to 16 bits and adds an assert to ensure that no information is lost when the entry is stored. Differential Revision: https://reviews.llvm.org/D70138