From 7bcb3bd169e7f3540601f05932336aac2ea291f6 Mon Sep 17 00:00:00 2001 From: Sjoerd Meijer Date: Fri, 25 Jun 2021 15:52:19 +0100 Subject: [PATCH 01/24] [AArch64] Added tests to neon-truncstore.ll. NFC. --- llvm/test/CodeGen/AArch64/neon-truncstore.ll | 227 +++++++++++++++++-- 1 file changed, 210 insertions(+), 17 deletions(-) diff --git a/llvm/test/CodeGen/AArch64/neon-truncstore.ll b/llvm/test/CodeGen/AArch64/neon-truncstore.ll index 2bbab0bfa3e56..7292841410a05 100644 --- a/llvm/test/CodeGen/AArch64/neon-truncstore.ll +++ b/llvm/test/CodeGen/AArch64/neon-truncstore.ll @@ -1,40 +1,233 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -mattr=+neon | FileCheck %s ; A vector TruncStore can not be selected. ; Test a trunc IR and a vector store IR can be selected correctly. -define void @truncStore.v2i64(<2 x i64> %a, <2 x i32>* %result) { -; CHECK-LABEL: truncStore.v2i64: -; CHECK: xtn v{{[0-9]+}}.2s, v{{[0-9]+}}.2d -; CHECK: {{st1 { v[0-9]+.2s }|str d[0-9]+}}, [x{{[0-9]+|sp}}] + +define void @v2i64_v2i32(<2 x i64> %a, <2 x i32>* %result) { +; CHECK-LABEL: v2i64_v2i32: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.2s, v0.2d +; CHECK-NEXT: str d0, [x0] +; CHECK-NEXT: ret %b = trunc <2 x i64> %a to <2 x i32> store <2 x i32> %b, <2 x i32>* %result ret void } -define void @truncStore.v4i32(<4 x i32> %a, <4 x i16>* %result) { -; CHECK-LABEL: truncStore.v4i32: -; CHECK: xtn v{{[0-9]+}}.4h, v{{[0-9]+}}.4s -; CHECK: {{st1 { v[0-9]+.4h }|str d[0-9]+}}, [x{{[0-9]+|sp}}] +define void @v4i64_v4i32(<4 x i64> %a, <4 x i32>* %result) { +; CHECK-LABEL: v4i64_v4i32: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.2s, v0.2d +; CHECK-NEXT: xtn2 v0.4s, v1.2d +; CHECK-NEXT: str q0, [x0] +; CHECK-NEXT: ret + %b = trunc <4 x i64> %a to <4 x i32> + store <4 x i32> %b, <4 x i32>* %result + ret void +} + +define void @v8i64_v8i32(<8 x i64> %a, <8 x i32>* %result) { +; CHECK-LABEL: v8i64_v8i32: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.2s, v0.2d +; CHECK-NEXT: xtn v2.2s, v2.2d +; CHECK-NEXT: xtn2 v0.4s, v1.2d +; CHECK-NEXT: xtn2 v2.4s, v3.2d +; CHECK-NEXT: stp q0, q2, [x0] +; CHECK-NEXT: ret + %b = trunc <8 x i64> %a to <8 x i32> + store <8 x i32> %b, <8 x i32>* %result + ret void +} + +define void @v2i32_v2i16(<2 x i32> %a, <2 x i16>* %result) { +; CHECK-LABEL: v2i32_v2i16: +; CHECK: // %bb.0: +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: mov w8, v0.s[1] +; CHECK-NEXT: fmov w9, s0 +; CHECK-NEXT: strh w8, [x0, #2] +; CHECK-NEXT: strh w9, [x0] +; CHECK-NEXT: ret + %b = trunc <2 x i32> %a to <2 x i16> + store <2 x i16> %b, <2 x i16>* %result + ret void +} + +define void @v4i32_v4i16(<4 x i32> %a, <4 x i16>* %result) { +; CHECK-LABEL: v4i32_v4i16: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: str d0, [x0] +; CHECK-NEXT: ret %b = trunc <4 x i32> %a to <4 x i16> store <4 x i16> %b, <4 x i16>* %result ret void } -define void @truncStore.v4i8(<4 x i32> %a, <4 x i8>* %result) { -; CHECK-LABEL: truncStore.v4i8: -; CHECK: xtn [[TMP:(v[0-9]+)]].4h, v{{[0-9]+}}.4s -; CHECK-NEXT: xtn [[TMP2:(v[0-9]+)]].8b, [[TMP]].8h -; CHECK-NEXT: str s{{[0-9]+}}, [x{{[0-9]+}}] +define void @v8i32_v8i16(<8 x i32> %a, <8 x i16>* %result) { +; CHECK-LABEL: v8i32_v8i16: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: xtn2 v0.8h, v1.4s +; CHECK-NEXT: str q0, [x0] +; CHECK-NEXT: ret + %b = trunc <8 x i32> %a to <8 x i16> + store <8 x i16> %b, <8 x i16>* %result + ret void +} + +define void @v16i32_v16i16(<16 x i32> %a, <16 x i16>* %result) { +; CHECK-LABEL: v16i32_v16i16: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: xtn v2.4h, v2.4s +; CHECK-NEXT: xtn2 v0.8h, v1.4s +; CHECK-NEXT: xtn2 v2.8h, v3.4s +; CHECK-NEXT: stp q0, q2, [x0] +; CHECK-NEXT: ret + %b = trunc <16 x i32> %a to <16 x i16> + store <16 x i16> %b, <16 x i16>* %result + ret void +} + +define void @v2i32_v2i8(<2 x i32> %a, <2 x i8>* %result) { +; CHECK-LABEL: v2i32_v2i8: +; CHECK: // %bb.0: +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: mov w8, v0.s[1] +; CHECK-NEXT: fmov w9, s0 +; CHECK-NEXT: strb w8, [x0, #1] +; CHECK-NEXT: strb w9, [x0] +; CHECK-NEXT: ret + %b = trunc <2 x i32> %a to <2 x i8> + store <2 x i8> %b, <2 x i8>* %result + ret void +} + +define void @v4i32_v4i8(<4 x i32> %a, <4 x i8>* %result) { +; CHECK-LABEL: v4i32_v4i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: str s0, [x0] +; CHECK-NEXT: ret %b = trunc <4 x i32> %a to <4 x i8> store <4 x i8> %b, <4 x i8>* %result ret void } -define void @truncStore.v8i16(<8 x i16> %a, <8 x i8>* %result) { -; CHECK-LABEL: truncStore.v8i16: -; CHECK: xtn v{{[0-9]+}}.8b, v{{[0-9]+}}.8h -; CHECK: {{st1 { v[0-9]+.8b }|str d[0-9]+}}, [x{{[0-9]+|sp}}] +define void @v8i32_v8i8(<8 x i32> %a, <8 x i8>* %result) { +; CHECK-LABEL: v8i32_v8i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: xtn2 v0.8h, v1.4s +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: str d0, [x0] +; CHECK-NEXT: ret + %b = trunc <8 x i32> %a to <8 x i8> + store <8 x i8> %b, <8 x i8>* %result + ret void +} + +define void @v16i32_v16i8(<16 x i32> %a, <16 x i8>* %result) { +; CHECK-LABEL: v16i32_v16i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v2.4h, v2.4s +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: xtn2 v2.8h, v3.4s +; CHECK-NEXT: xtn2 v0.8h, v1.4s +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: xtn2 v0.16b, v2.8h +; CHECK-NEXT: str q0, [x0] +; CHECK-NEXT: ret + %b = trunc <16 x i32> %a to <16 x i8> + store <16 x i8> %b, <16 x i8>* %result + ret void +} + +define void @v32i32_v32i8(<32 x i32> %a, <32 x i8>* %result) { +; CHECK-LABEL: v32i32_v32i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v2.4h, v2.4s +; CHECK-NEXT: xtn v0.4h, v0.4s +; CHECK-NEXT: xtn2 v2.8h, v3.4s +; CHECK-NEXT: xtn2 v0.8h, v1.4s +; CHECK-NEXT: xtn v6.4h, v6.4s +; CHECK-NEXT: xtn v4.4h, v4.4s +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: xtn2 v0.16b, v2.8h +; CHECK-NEXT: xtn2 v6.8h, v7.4s +; CHECK-NEXT: xtn2 v4.8h, v5.4s +; CHECK-NEXT: xtn v1.8b, v4.8h +; CHECK-NEXT: xtn2 v1.16b, v6.8h +; CHECK-NEXT: stp q0, q1, [x0] +; CHECK-NEXT: ret + %b = trunc <32 x i32> %a to <32 x i8> + store <32 x i8> %b, <32 x i8>* %result + ret void +} + +define void @v2i16_v2i8(<2 x i16> %a, <2 x i8>* %result) { +; CHECK-LABEL: v2i16_v2i8: +; CHECK: // %bb.0: +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: mov w8, v0.s[1] +; CHECK-NEXT: fmov w9, s0 +; CHECK-NEXT: strb w8, [x0, #1] +; CHECK-NEXT: strb w9, [x0] +; CHECK-NEXT: ret + %b = trunc <2 x i16> %a to <2 x i8> + store <2 x i8> %b, <2 x i8>* %result + ret void +} + +define void @v4i16_v4i8(<4 x i16> %a, <4 x i8>* %result) { +; CHECK-LABEL: v4i16_v4i8: +; CHECK: // %bb.0: +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: str s0, [x0] +; CHECK-NEXT: ret + %b = trunc <4 x i16> %a to <4 x i8> + store <4 x i8> %b, <4 x i8>* %result + ret void +} + +define void @v8i16_v8i8(<8 x i16> %a, <8 x i8>* %result) { +; CHECK-LABEL: v8i16_v8i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: str d0, [x0] +; CHECK-NEXT: ret %b = trunc <8 x i16> %a to <8 x i8> store <8 x i8> %b, <8 x i8>* %result ret void } + +define void @v16i16_v16i8(<16 x i16> %a, <16 x i8>* %result) { +; CHECK-LABEL: v16i16_v16i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: xtn2 v0.16b, v1.8h +; CHECK-NEXT: str q0, [x0] +; CHECK-NEXT: ret + %b = trunc <16 x i16> %a to <16 x i8> + store <16 x i8> %b, <16 x i8>* %result + ret void +} + +define void @v32i16_v32i8(<32 x i16> %a, <32 x i8>* %result) { +; CHECK-LABEL: v32i16_v32i8: +; CHECK: // %bb.0: +; CHECK-NEXT: xtn v0.8b, v0.8h +; CHECK-NEXT: xtn v2.8b, v2.8h +; CHECK-NEXT: xtn2 v0.16b, v1.8h +; CHECK-NEXT: xtn2 v2.16b, v3.8h +; CHECK-NEXT: stp q0, q2, [x0] +; CHECK-NEXT: ret + %b = trunc <32 x i16> %a to <32 x i8> + store <32 x i8> %b, <32 x i8>* %result + ret void +} From 3a7cea2858ff2665c5430ead186a45a7f7a2d112 Mon Sep 17 00:00:00 2001 From: Sjoerd Meijer Date: Mon, 28 Jun 2021 17:25:53 +0100 Subject: [PATCH 02/24] Revert "[AArch64] Custom lower <4 x i8> loads" This reverts commit 51e434fc2590d1d3ffa6545cd07290a238db2b88 because of a build bot failure in test-suite::GCC-C-execute-pr60960.test that I need to investigate. --- .../Target/AArch64/AArch64ISelLowering.cpp | 43 +--- llvm/lib/Target/AArch64/AArch64ISelLowering.h | 1 - llvm/test/CodeGen/AArch64/aarch64-load-ext.ll | 196 ++---------------- llvm/test/CodeGen/AArch64/arm64-vshift.ll | 33 +-- llvm/test/CodeGen/AArch64/neon-extload.ll | 145 +++++++++++++ llvm/test/CodeGen/AArch64/sadd_sat_vec.ll | 20 +- llvm/test/CodeGen/AArch64/ssub_sat_vec.ll | 20 +- llvm/test/CodeGen/AArch64/uadd_sat_vec.ll | 20 +- llvm/test/CodeGen/AArch64/usub_sat_vec.ll | 20 +- 9 files changed, 245 insertions(+), 253 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/neon-extload.ll diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 16bb7eb222723..9ceb91ea8017a 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -1131,13 +1131,6 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::VSCALE, MVT::i32, Custom); setTruncStoreAction(MVT::v4i16, MVT::v4i8, Custom); - - setLoadExtAction(ISD::EXTLOAD, MVT::v4i16, MVT::v4i8, Custom); - setLoadExtAction(ISD::SEXTLOAD, MVT::v4i16, MVT::v4i8, Custom); - setLoadExtAction(ISD::ZEXTLOAD, MVT::v4i16, MVT::v4i8, Custom); - setLoadExtAction(ISD::EXTLOAD, MVT::v4i32, MVT::v4i8, Custom); - setLoadExtAction(ISD::SEXTLOAD, MVT::v4i32, MVT::v4i8, Custom); - setLoadExtAction(ISD::ZEXTLOAD, MVT::v4i32, MVT::v4i8, Custom); } if (Subtarget->hasSVE()) { @@ -4483,40 +4476,6 @@ SDValue AArch64TargetLowering::LowerSTORE(SDValue Op, return SDValue(); } -// Custom lowering for extending v4i8 vector loads. -SDValue AArch64TargetLowering::LowerLOAD(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - LoadSDNode *LoadNode = cast(Op); - assert(LoadNode && "Expected custom lowering of a load node"); - EVT VT = Op->getValueType(0); - assert((VT == MVT::v4i16 || VT == MVT::v4i32) && "Expected v4i16 or v4i32"); - - if (LoadNode->getMemoryVT() != MVT::v4i8) - return SDValue(); - - unsigned ExtType; - if (LoadNode->getExtensionType() == ISD::SEXTLOAD) - ExtType = ISD::SIGN_EXTEND; - else if (LoadNode->getExtensionType() == ISD::ZEXTLOAD || - LoadNode->getExtensionType() == ISD::EXTLOAD) - ExtType = ISD::ZERO_EXTEND; - else - return SDValue(); - - SDValue Load = DAG.getLoad(MVT::f32, DL, DAG.getEntryNode(), - LoadNode->getBasePtr(), MachinePointerInfo()); - SDValue Chain = Load.getValue(1); - SDValue Vec = DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2f32, Load); - SDValue BC = DAG.getNode(ISD::BITCAST, DL, MVT::v8i8, Vec); - SDValue Ext = DAG.getNode(ExtType, DL, MVT::v8i16, BC); - Ext = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, MVT::v4i16, Ext, - DAG.getConstant(0, DL, MVT::i64)); - if (VT == MVT::v4i32) - Ext = DAG.getNode(ExtType, DL, MVT::v4i32, Ext); - return DAG.getMergeValues({Ext, Chain}, DL); -} - // Generate SUBS and CSEL for integer abs. SDValue AArch64TargetLowering::LowerABS(SDValue Op, SelectionDAG &DAG) const { MVT VT = Op.getSimpleValueType(); @@ -4760,7 +4719,7 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, case ISD::LOAD: if (useSVEForFixedLengthVectorVT(Op.getValueType())) return LowerFixedLengthVectorLoadToSVE(Op, DAG); - return LowerLOAD(Op, DAG); + llvm_unreachable("Unexpected request to lower ISD::LOAD"); case ISD::ADD: return LowerToPredicatedOp(Op, DAG, AArch64ISD::ADD_PRED); case ISD::AND: diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 7daa61996739f..f3b2da8304303 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -851,7 +851,6 @@ class AArch64TargetLowering : public TargetLowering { SmallVectorImpl &InVals, bool isThisReturn, SDValue ThisVal) const; - SDValue LowerLOAD(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSTORE(SDValue Op, SelectionDAG &DAG) const; SDValue LowerABS(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/test/CodeGen/AArch64/aarch64-load-ext.ll b/llvm/test/CodeGen/AArch64/aarch64-load-ext.ll index d303ab8b80f3c..308352e3e2277 100644 --- a/llvm/test/CodeGen/AArch64/aarch64-load-ext.ll +++ b/llvm/test/CodeGen/AArch64/aarch64-load-ext.ll @@ -86,195 +86,27 @@ define <2 x i8> @test3(<2 x i8>* %v2i8_ptr) { define <4 x i8> @test4(<4 x i8>* %v4i8_ptr) { ; CHECK-LE-LABEL: test4: ; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: ushll v0.8h, v0.8b, #0 +; CHECK-LE-NEXT: ld1 { v0.b }[0], [x0] +; CHECK-LE-NEXT: add x8, x0, #1 // =1 +; CHECK-LE-NEXT: ld1 { v0.b }[2], [x8] +; CHECK-LE-NEXT: add x8, x0, #2 // =2 +; CHECK-LE-NEXT: ld1 { v0.b }[4], [x8] +; CHECK-LE-NEXT: add x8, x0, #3 // =3 +; CHECK-LE-NEXT: ld1 { v0.b }[6], [x8] ; CHECK-LE-NEXT: // kill: def $d0 killed $d0 killed $q0 ; CHECK-LE-NEXT: ret ; ; CHECK-BE-LABEL: test4: ; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: ushll v0.8h, v0.8b, #0 +; CHECK-BE-NEXT: ld1 { v0.b }[0], [x0] +; CHECK-BE-NEXT: add x8, x0, #1 // =1 +; CHECK-BE-NEXT: ld1 { v0.b }[2], [x8] +; CHECK-BE-NEXT: add x8, x0, #2 // =2 +; CHECK-BE-NEXT: ld1 { v0.b }[4], [x8] +; CHECK-BE-NEXT: add x8, x0, #3 // =3 +; CHECK-BE-NEXT: ld1 { v0.b }[6], [x8] ; CHECK-BE-NEXT: rev64 v0.4h, v0.4h ; CHECK-BE-NEXT: ret %v4i8 = load <4 x i8>, <4 x i8>* %v4i8_ptr ret <4 x i8> %v4i8 } - -define <4 x i32> @fsext_v4i32(<4 x i8>* %a) { -; CHECK-LE-LABEL: fsext_v4i32: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: sshll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: sshll v0.4s, v0.4h, #0 -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: fsext_v4i32: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: sshll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: sshll v0.4s, v0.4h, #0 -; CHECK-BE-NEXT: rev64 v0.4s, v0.4s -; CHECK-BE-NEXT: ext v0.16b, v0.16b, v0.16b, #8 -; CHECK-BE-NEXT: ret - %x = load <4 x i8>, <4 x i8>* %a - %y = sext <4 x i8> %x to <4 x i32> - ret <4 x i32> %y -} - -define <4 x i32> @fzext_v4i32(<4 x i8>* %a) { -; CHECK-LE-LABEL: fzext_v4i32: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: ushll v0.4s, v0.4h, #0 -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: fzext_v4i32: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: ushll v0.4s, v0.4h, #0 -; CHECK-BE-NEXT: rev64 v0.4s, v0.4s -; CHECK-BE-NEXT: ext v0.16b, v0.16b, v0.16b, #8 -; CHECK-BE-NEXT: ret - %x = load <4 x i8>, <4 x i8>* %a - %y = zext <4 x i8> %x to <4 x i32> - ret <4 x i32> %y -} - -; TODO: This codegen could just be: -; ldrb w0, [x0] -; -define i32 @loadExti32(<4 x i8>* %ref) { -; CHECK-LE-LABEL: loadExti32: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: umov w8, v0.h[0] -; CHECK-LE-NEXT: and w0, w8, #0xff -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: loadExti32: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: umov w8, v0.h[0] -; CHECK-BE-NEXT: and w0, w8, #0xff -; CHECK-BE-NEXT: ret - %a = load <4 x i8>, <4 x i8>* %ref - %vecext = extractelement <4 x i8> %a, i32 0 - %conv = zext i8 %vecext to i32 - ret i32 %conv -} - -define <4 x i16> @fsext_v4i16(<4 x i8>* %a) { -; CHECK-LE-LABEL: fsext_v4i16: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: sshll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: // kill: def $d0 killed $d0 killed $q0 -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: fsext_v4i16: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: sshll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: rev64 v0.4h, v0.4h -; CHECK-BE-NEXT: ret - %x = load <4 x i8>, <4 x i8>* %a - %y = sext <4 x i8> %x to <4 x i16> - ret <4 x i16> %y -} - -define <4 x i16> @fzext_v4i16(<4 x i8>* %a) { -; CHECK-LE-LABEL: fzext_v4i16: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: // kill: def $d0 killed $d0 killed $q0 -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: fzext_v4i16: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: rev64 v0.4h, v0.4h -; CHECK-BE-NEXT: ret - %x = load <4 x i8>, <4 x i8>* %a - %y = zext <4 x i8> %x to <4 x i16> - ret <4 x i16> %y -} - -define <4 x i16> @anyext_v4i16(<4 x i8> *%a, <4 x i8> *%b) { -; CHECK-LE-LABEL: anyext_v4i16: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: ldr s1, [x1] -; CHECK-LE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: ushll v1.8h, v1.8b, #0 -; CHECK-LE-NEXT: add v0.4h, v0.4h, v1.4h -; CHECK-LE-NEXT: shl v0.4h, v0.4h, #8 -; CHECK-LE-NEXT: sshr v0.4h, v0.4h, #8 -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: anyext_v4i16: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: ldr s1, [x1] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: rev32 v1.8b, v1.8b -; CHECK-BE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: ushll v1.8h, v1.8b, #0 -; CHECK-BE-NEXT: add v0.4h, v0.4h, v1.4h -; CHECK-BE-NEXT: shl v0.4h, v0.4h, #8 -; CHECK-BE-NEXT: sshr v0.4h, v0.4h, #8 -; CHECK-BE-NEXT: rev64 v0.4h, v0.4h -; CHECK-BE-NEXT: ret - %x = load <4 x i8>, <4 x i8>* %a, align 4 - %y = load <4 x i8>, <4 x i8>* %b, align 4 - %z = add <4 x i8> %x, %y - %s = sext <4 x i8> %z to <4 x i16> - ret <4 x i16> %s -} - -define <4 x i32> @anyext_v4i32(<4 x i8> *%a, <4 x i8> *%b) { -; CHECK-LE-LABEL: anyext_v4i32: -; CHECK-LE: // %bb.0: -; CHECK-LE-NEXT: ldr s0, [x0] -; CHECK-LE-NEXT: ldr s1, [x1] -; CHECK-LE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-LE-NEXT: ushll v1.8h, v1.8b, #0 -; CHECK-LE-NEXT: add v0.4h, v0.4h, v1.4h -; CHECK-LE-NEXT: ushll v0.4s, v0.4h, #0 -; CHECK-LE-NEXT: shl v0.4s, v0.4s, #24 -; CHECK-LE-NEXT: sshr v0.4s, v0.4s, #24 -; CHECK-LE-NEXT: ret -; -; CHECK-BE-LABEL: anyext_v4i32: -; CHECK-BE: // %bb.0: -; CHECK-BE-NEXT: ldr s0, [x0] -; CHECK-BE-NEXT: ldr s1, [x1] -; CHECK-BE-NEXT: rev32 v0.8b, v0.8b -; CHECK-BE-NEXT: rev32 v1.8b, v1.8b -; CHECK-BE-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-BE-NEXT: ushll v1.8h, v1.8b, #0 -; CHECK-BE-NEXT: add v0.4h, v0.4h, v1.4h -; CHECK-BE-NEXT: ushll v0.4s, v0.4h, #0 -; CHECK-BE-NEXT: shl v0.4s, v0.4s, #24 -; CHECK-BE-NEXT: sshr v0.4s, v0.4s, #24 -; CHECK-BE-NEXT: rev64 v0.4s, v0.4s -; CHECK-BE-NEXT: ext v0.16b, v0.16b, v0.16b, #8 -; CHECK-BE-NEXT: ret - %x = load <4 x i8>, <4 x i8>* %a, align 4 - %y = load <4 x i8>, <4 x i8>* %b, align 4 - %z = add <4 x i8> %x, %y - %s = sext <4 x i8> %z to <4 x i32> - ret <4 x i32> %s -} diff --git a/llvm/test/CodeGen/AArch64/arm64-vshift.ll b/llvm/test/CodeGen/AArch64/arm64-vshift.ll index 07b257043426d..c63f3399e636f 100644 --- a/llvm/test/CodeGen/AArch64/arm64-vshift.ll +++ b/llvm/test/CodeGen/AArch64/arm64-vshift.ll @@ -1494,12 +1494,17 @@ define <8 x i16> @neon.ushl8h_no_constant_shift(<8 x i8>* %A) nounwind { } define <4 x i32> @neon.ushl8h_constant_shift_extend_not_2x(<4 x i8>* %A) nounwind { -; CHECK-LABEL: neon.ushl8h_constant_shift_extend_not_2x: -; CHECK: // %bb.0: -; CHECK-NEXT: ldr s0, [x0] -; CHECK-NEXT: ushll.8h v0, v0, #0 -; CHECK-NEXT: ushll.4s v0, v0, #1 -; CHECK-NEXT: ret +;CHECK-LABEL: @neon.ushl8h_constant_shift_extend_not_2x +;CHECK-NOT: ushll.8h v0, +;CHECK: ldrb w8, [x0] +;CHECK: fmov s0, w8 +;CHECK: ldrb w8, [x0, #1] +;CHECK: mov.s v0[1], w8 +;CHECK: ldrb w8, [x0, #2] +;CHECK: mov.s v0[2], w8 +;CHECK: ldrb w8, [x0, #3] +;CHECK: mov.s v0[3], w8 +;CHECK: shl.4s v0, v0, #1 %tmp1 = load <4 x i8>, <4 x i8>* %A %tmp2 = zext <4 x i8> %tmp1 to <4 x i32> %tmp3 = call <4 x i32> @llvm.aarch64.neon.ushl.v4i32(<4 x i32> %tmp2, <4 x i32> ) @@ -1632,12 +1637,16 @@ define <8 x i16> @neon.sshll8h_constant_shift(<8 x i8>* %A) nounwind { } define <4 x i32> @neon.sshl4s_wrong_ext_constant_shift(<4 x i8>* %A) nounwind { -; CHECK-LABEL: neon.sshl4s_wrong_ext_constant_shift: -; CHECK: // %bb.0: -; CHECK-NEXT: ldr s0, [x0] -; CHECK-NEXT: sshll.8h v0, v0, #0 -; CHECK-NEXT: sshll.4s v0, v0, #1 -; CHECK-NEXT: ret +;CHECK-LABEL: neon.sshl4s_wrong_ext_constant_shift +;CHECK: ldrsb w8, [x0] +;CHECK-NEXT: fmov s0, w8 +;CHECK-NEXT: ldrsb w8, [x0, #1] +;CHECK-NEXT: mov.s v0[1], w8 +;CHECK-NEXT: ldrsb w8, [x0, #2] +;CHECK-NEXT: mov.s v0[2], w8 +;CHECK-NEXT: ldrsb w8, [x0, #3] +;CHECK-NEXT: mov.s v0[3], w8 +;CHECK-NEXT: shl.4s v0, v0, #1 %tmp1 = load <4 x i8>, <4 x i8>* %A %tmp2 = sext <4 x i8> %tmp1 to <4 x i32> %tmp3 = call <4 x i32> @llvm.aarch64.neon.sshl.v4i32(<4 x i32> %tmp2, <4 x i32> ) diff --git a/llvm/test/CodeGen/AArch64/neon-extload.ll b/llvm/test/CodeGen/AArch64/neon-extload.ll new file mode 100644 index 0000000000000..321a1babb411d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/neon-extload.ll @@ -0,0 +1,145 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -mattr=+neon | FileCheck %s --check-prefix=LE +; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64_be-none-linux-gnu -mattr=+neon | FileCheck %s --check-prefix=BE + +define <4 x i32> @fsext_v4i32(<4 x i8>* %a) { +; LE-LABEL: fsext_v4i32: +; LE: // %bb.0: +; LE-NEXT: ldrsb w8, [x0] +; LE-NEXT: ldrsb w9, [x0, #1] +; LE-NEXT: ldrsb w10, [x0, #2] +; LE-NEXT: ldrsb w11, [x0, #3] +; LE-NEXT: fmov s0, w8 +; LE-NEXT: mov v0.s[1], w9 +; LE-NEXT: mov v0.s[2], w10 +; LE-NEXT: mov v0.s[3], w11 +; LE-NEXT: ret +; +; BE-LABEL: fsext_v4i32: +; BE: // %bb.0: +; BE-NEXT: ldrsb w8, [x0] +; BE-NEXT: ldrsb w9, [x0, #1] +; BE-NEXT: ldrsb w10, [x0, #2] +; BE-NEXT: ldrsb w11, [x0, #3] +; BE-NEXT: fmov s0, w8 +; BE-NEXT: mov v0.s[1], w9 +; BE-NEXT: mov v0.s[2], w10 +; BE-NEXT: mov v0.s[3], w11 +; BE-NEXT: rev64 v0.4s, v0.4s +; BE-NEXT: ext v0.16b, v0.16b, v0.16b, #8 +; BE-NEXT: ret + %x = load <4 x i8>, <4 x i8>* %a + %y = sext <4 x i8> %x to <4 x i32> + ret <4 x i32> %y +} + +define <4 x i32> @fzext_v4i32(<4 x i8>* %a) { +; LE-LABEL: fzext_v4i32: +; LE: // %bb.0: +; LE-NEXT: ldrb w8, [x0] +; LE-NEXT: ldrb w9, [x0, #1] +; LE-NEXT: ldrb w10, [x0, #2] +; LE-NEXT: ldrb w11, [x0, #3] +; LE-NEXT: fmov s0, w8 +; LE-NEXT: mov v0.s[1], w9 +; LE-NEXT: mov v0.s[2], w10 +; LE-NEXT: mov v0.s[3], w11 +; LE-NEXT: ret +; +; BE-LABEL: fzext_v4i32: +; BE: // %bb.0: +; BE-NEXT: ldrb w8, [x0] +; BE-NEXT: ldrb w9, [x0, #1] +; BE-NEXT: ldrb w10, [x0, #2] +; BE-NEXT: ldrb w11, [x0, #3] +; BE-NEXT: fmov s0, w8 +; BE-NEXT: mov v0.s[1], w9 +; BE-NEXT: mov v0.s[2], w10 +; BE-NEXT: mov v0.s[3], w11 +; BE-NEXT: rev64 v0.4s, v0.4s +; BE-NEXT: ext v0.16b, v0.16b, v0.16b, #8 +; BE-NEXT: ret + %x = load <4 x i8>, <4 x i8>* %a + %y = zext <4 x i8> %x to <4 x i32> + ret <4 x i32> %y +} + +define i32 @loadExt.i32(<4 x i8>* %ref) { +; CHECK-LABEL: loadExt.i32: +; CHECK: ldrb +; LE-LABEL: loadExt.i32: +; LE: // %bb.0: +; LE-NEXT: ldrb w0, [x0] +; LE-NEXT: ret +; +; BE-LABEL: loadExt.i32: +; BE: // %bb.0: +; BE-NEXT: ldrb w0, [x0] +; BE-NEXT: ret + %a = load <4 x i8>, <4 x i8>* %ref + %vecext = extractelement <4 x i8> %a, i32 0 + %conv = zext i8 %vecext to i32 + ret i32 %conv +} + +define <4 x i16> @fsext_v4i16(<4 x i8>* %a) { +; LE-LABEL: fsext_v4i16: +; LE: // %bb.0: +; LE-NEXT: ldrsb w8, [x0] +; LE-NEXT: ldrsb w9, [x0, #1] +; LE-NEXT: ldrsb w10, [x0, #2] +; LE-NEXT: ldrsb w11, [x0, #3] +; LE-NEXT: fmov s0, w8 +; LE-NEXT: mov v0.h[1], w9 +; LE-NEXT: mov v0.h[2], w10 +; LE-NEXT: mov v0.h[3], w11 +; LE-NEXT: // kill: def $d0 killed $d0 killed $q0 +; LE-NEXT: ret +; +; BE-LABEL: fsext_v4i16: +; BE: // %bb.0: +; BE-NEXT: ldrsb w8, [x0] +; BE-NEXT: ldrsb w9, [x0, #1] +; BE-NEXT: ldrsb w10, [x0, #2] +; BE-NEXT: ldrsb w11, [x0, #3] +; BE-NEXT: fmov s0, w8 +; BE-NEXT: mov v0.h[1], w9 +; BE-NEXT: mov v0.h[2], w10 +; BE-NEXT: mov v0.h[3], w11 +; BE-NEXT: rev64 v0.4h, v0.4h +; BE-NEXT: ret + %x = load <4 x i8>, <4 x i8>* %a + %y = sext <4 x i8> %x to <4 x i16> + ret <4 x i16> %y +} + +define <4 x i16> @fzext_v4i16(<4 x i8>* %a) { +; LE-LABEL: fzext_v4i16: +; LE: // %bb.0: +; LE-NEXT: ldrb w8, [x0] +; LE-NEXT: ldrb w9, [x0, #1] +; LE-NEXT: ldrb w10, [x0, #2] +; LE-NEXT: ldrb w11, [x0, #3] +; LE-NEXT: fmov s0, w8 +; LE-NEXT: mov v0.h[1], w9 +; LE-NEXT: mov v0.h[2], w10 +; LE-NEXT: mov v0.h[3], w11 +; LE-NEXT: // kill: def $d0 killed $d0 killed $q0 +; LE-NEXT: ret +; +; BE-LABEL: fzext_v4i16: +; BE: // %bb.0: +; BE-NEXT: ldrb w8, [x0] +; BE-NEXT: ldrb w9, [x0, #1] +; BE-NEXT: ldrb w10, [x0, #2] +; BE-NEXT: ldrb w11, [x0, #3] +; BE-NEXT: fmov s0, w8 +; BE-NEXT: mov v0.h[1], w9 +; BE-NEXT: mov v0.h[2], w10 +; BE-NEXT: mov v0.h[3], w11 +; BE-NEXT: rev64 v0.4h, v0.4h +; BE-NEXT: ret + %x = load <4 x i8>, <4 x i8>* %a + %y = zext <4 x i8> %x to <4 x i16> + ret <4 x i16> %y +} diff --git a/llvm/test/CodeGen/AArch64/sadd_sat_vec.ll b/llvm/test/CodeGen/AArch64/sadd_sat_vec.ll index 9c654f6719b18..cefd4758b3747 100644 --- a/llvm/test/CodeGen/AArch64/sadd_sat_vec.ll +++ b/llvm/test/CodeGen/AArch64/sadd_sat_vec.ll @@ -112,10 +112,22 @@ define void @v8i8(<8 x i8>* %px, <8 x i8>* %py, <8 x i8>* %pz) nounwind { define void @v4i8(<4 x i8>* %px, <4 x i8>* %py, <4 x i8>* %pz) nounwind { ; CHECK-LABEL: v4i8: ; CHECK: // %bb.0: -; CHECK-NEXT: ldr s0, [x0] -; CHECK-NEXT: ldr s1, [x1] -; CHECK-NEXT: sshll v0.8h, v0.8b, #0 -; CHECK-NEXT: sshll v1.8h, v1.8b, #0 +; CHECK-NEXT: ldrsb w8, [x0] +; CHECK-NEXT: ldrsb w9, [x1] +; CHECK-NEXT: ldrsb w10, [x0, #1] +; CHECK-NEXT: ldrsb w11, [x1, #1] +; CHECK-NEXT: fmov s0, w8 +; CHECK-NEXT: fmov s1, w9 +; CHECK-NEXT: ldrsb w8, [x0, #2] +; CHECK-NEXT: ldrsb w9, [x1, #2] +; CHECK-NEXT: mov v0.h[1], w10 +; CHECK-NEXT: mov v1.h[1], w11 +; CHECK-NEXT: ldrsb w10, [x0, #3] +; CHECK-NEXT: ldrsb w11, [x1, #3] +; CHECK-NEXT: mov v0.h[2], w8 +; CHECK-NEXT: mov v1.h[2], w9 +; CHECK-NEXT: mov v0.h[3], w10 +; CHECK-NEXT: mov v1.h[3], w11 ; CHECK-NEXT: shl v1.4h, v1.4h, #8 ; CHECK-NEXT: shl v0.4h, v0.4h, #8 ; CHECK-NEXT: sqadd v0.4h, v0.4h, v1.4h diff --git a/llvm/test/CodeGen/AArch64/ssub_sat_vec.ll b/llvm/test/CodeGen/AArch64/ssub_sat_vec.ll index 7c2e2330608e8..17af8a11aeee5 100644 --- a/llvm/test/CodeGen/AArch64/ssub_sat_vec.ll +++ b/llvm/test/CodeGen/AArch64/ssub_sat_vec.ll @@ -113,10 +113,22 @@ define void @v8i8(<8 x i8>* %px, <8 x i8>* %py, <8 x i8>* %pz) nounwind { define void @v4i8(<4 x i8>* %px, <4 x i8>* %py, <4 x i8>* %pz) nounwind { ; CHECK-LABEL: v4i8: ; CHECK: // %bb.0: -; CHECK-NEXT: ldr s0, [x0] -; CHECK-NEXT: ldr s1, [x1] -; CHECK-NEXT: sshll v0.8h, v0.8b, #0 -; CHECK-NEXT: sshll v1.8h, v1.8b, #0 +; CHECK-NEXT: ldrsb w8, [x0] +; CHECK-NEXT: ldrsb w9, [x1] +; CHECK-NEXT: ldrsb w10, [x0, #1] +; CHECK-NEXT: ldrsb w11, [x1, #1] +; CHECK-NEXT: fmov s0, w8 +; CHECK-NEXT: fmov s1, w9 +; CHECK-NEXT: ldrsb w8, [x0, #2] +; CHECK-NEXT: ldrsb w9, [x1, #2] +; CHECK-NEXT: mov v0.h[1], w10 +; CHECK-NEXT: mov v1.h[1], w11 +; CHECK-NEXT: ldrsb w10, [x0, #3] +; CHECK-NEXT: ldrsb w11, [x1, #3] +; CHECK-NEXT: mov v0.h[2], w8 +; CHECK-NEXT: mov v1.h[2], w9 +; CHECK-NEXT: mov v0.h[3], w10 +; CHECK-NEXT: mov v1.h[3], w11 ; CHECK-NEXT: shl v1.4h, v1.4h, #8 ; CHECK-NEXT: shl v0.4h, v0.4h, #8 ; CHECK-NEXT: sqsub v0.4h, v0.4h, v1.4h diff --git a/llvm/test/CodeGen/AArch64/uadd_sat_vec.ll b/llvm/test/CodeGen/AArch64/uadd_sat_vec.ll index 2b52e4c934c9d..21427a6a92d7e 100644 --- a/llvm/test/CodeGen/AArch64/uadd_sat_vec.ll +++ b/llvm/test/CodeGen/AArch64/uadd_sat_vec.ll @@ -112,11 +112,23 @@ define void @v8i8(<8 x i8>* %px, <8 x i8>* %py, <8 x i8>* %pz) nounwind { define void @v4i8(<4 x i8>* %px, <4 x i8>* %py, <4 x i8>* %pz) nounwind { ; CHECK-LABEL: v4i8: ; CHECK: // %bb.0: -; CHECK-NEXT: ldr s0, [x0] -; CHECK-NEXT: ldr s1, [x1] +; CHECK-NEXT: ldrb w8, [x0] +; CHECK-NEXT: ldrb w9, [x1] +; CHECK-NEXT: ldrb w10, [x0, #1] +; CHECK-NEXT: ldrb w11, [x1, #1] +; CHECK-NEXT: ldrb w12, [x0, #2] +; CHECK-NEXT: fmov s0, w8 +; CHECK-NEXT: ldrb w8, [x1, #2] +; CHECK-NEXT: fmov s1, w9 +; CHECK-NEXT: mov v0.h[1], w10 +; CHECK-NEXT: ldrb w9, [x0, #3] +; CHECK-NEXT: ldrb w10, [x1, #3] +; CHECK-NEXT: mov v1.h[1], w11 +; CHECK-NEXT: mov v0.h[2], w12 +; CHECK-NEXT: mov v1.h[2], w8 +; CHECK-NEXT: mov v0.h[3], w9 +; CHECK-NEXT: mov v1.h[3], w10 ; CHECK-NEXT: movi d2, #0xff00ff00ff00ff -; CHECK-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-NEXT: ushll v1.8h, v1.8b, #0 ; CHECK-NEXT: add v0.4h, v0.4h, v1.4h ; CHECK-NEXT: umin v0.4h, v0.4h, v2.4h ; CHECK-NEXT: xtn v0.8b, v0.8h diff --git a/llvm/test/CodeGen/AArch64/usub_sat_vec.ll b/llvm/test/CodeGen/AArch64/usub_sat_vec.ll index 63bbac3be3fb8..a0ab8040e8fc0 100644 --- a/llvm/test/CodeGen/AArch64/usub_sat_vec.ll +++ b/llvm/test/CodeGen/AArch64/usub_sat_vec.ll @@ -113,10 +113,22 @@ define void @v8i8(<8 x i8>* %px, <8 x i8>* %py, <8 x i8>* %pz) nounwind { define void @v4i8(<4 x i8>* %px, <4 x i8>* %py, <4 x i8>* %pz) nounwind { ; CHECK-LABEL: v4i8: ; CHECK: // %bb.0: -; CHECK-NEXT: ldr s0, [x0] -; CHECK-NEXT: ldr s1, [x1] -; CHECK-NEXT: ushll v0.8h, v0.8b, #0 -; CHECK-NEXT: ushll v1.8h, v1.8b, #0 +; CHECK-NEXT: ldrb w8, [x0] +; CHECK-NEXT: ldrb w9, [x1] +; CHECK-NEXT: ldrb w10, [x0, #1] +; CHECK-NEXT: ldrb w11, [x1, #1] +; CHECK-NEXT: fmov s0, w8 +; CHECK-NEXT: fmov s1, w9 +; CHECK-NEXT: ldrb w8, [x0, #2] +; CHECK-NEXT: ldrb w9, [x1, #2] +; CHECK-NEXT: mov v0.h[1], w10 +; CHECK-NEXT: mov v1.h[1], w11 +; CHECK-NEXT: ldrb w10, [x0, #3] +; CHECK-NEXT: ldrb w11, [x1, #3] +; CHECK-NEXT: mov v0.h[2], w8 +; CHECK-NEXT: mov v1.h[2], w9 +; CHECK-NEXT: mov v0.h[3], w10 +; CHECK-NEXT: mov v1.h[3], w11 ; CHECK-NEXT: uqsub v0.4h, v0.4h, v1.4h ; CHECK-NEXT: xtn v0.8b, v0.8h ; CHECK-NEXT: str s0, [x2] From 2dca0b5a1ce431692136b293fd5f9ecadea31750 Mon Sep 17 00:00:00 2001 From: Anirudh Prasad Date: Mon, 28 Jun 2021 12:46:31 -0400 Subject: [PATCH 03/24] [AsmParser][SystemZ][z/OS] Fix hanging scenario in HLASMAsmParser class - In the caller of the overridden `parseStatement` function (i.e. the `AsmParser::Run()`) in the case of an error **and** if we're not at the start of the statement, we "eat" up until the end of the current statement, so we don't have to process it again. - However, in the HLASMAsmParser class what's happening is that, if an error occurs at the very start of the statement (for example, you invoke the HLASMAsmParser to parse a gnu directive), we will error out, but we never really progress in terms of the next token in the statement to parse. We simply keep looping processing the same error over and over again (partly because we're at the start of the statement) - To remedy this, when the `parseAsHLASMLabel` function fails, before returning, we "eat" until the end of the statement function, so we don't process it anymore. Reviewed By: uweigand Differential Revision: https://reviews.llvm.org/D104869 --- llvm/lib/MC/MCParser/AsmParser.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 5305dde360d4f..1adde169c0a16 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -6302,8 +6302,12 @@ bool HLASMAsmParser::parseStatement(ParseStatementInfo &Info, if (ShouldParseAsHLASMLabel) { // If there were any errors while handling and emitting the label, // early return. - if (parseAsHLASMLabel(Info, SI)) + if (parseAsHLASMLabel(Info, SI)) { + // If we know we've failed in parsing, simply eat until end of the + // statement. This ensures that we don't process any other statements. + eatToEndOfStatement(); return true; + } } return parseAsMachineInstruction(Info, SI); From 88d5eba139598b51718b5a10b336e0bc9f51aff4 Mon Sep 17 00:00:00 2001 From: Stephan Herhut Date: Mon, 28 Jun 2021 18:45:29 +0200 Subject: [PATCH 04/24] Revert "Revert "[mlir][memref] Implement lowering of memref.copy to llvm"" This reverts commit 7d6e589fc86d7865fc4bf92c583209700dd32aac. Windows build was unbroken. --- .../mlir/Dialect/LLVMIR/FunctionCallUtils.h | 2 + .../mlir/ExecutionEngine/CRunnerUtils.h | 7 +++ .../StandardToLLVM/StandardToLLVM.cpp | 63 +++++++++++++++++++ .../Dialect/LLVMIR/IR/FunctionCallUtils.cpp | 10 +++ mlir/lib/ExecutionEngine/CRunnerUtils.cpp | 47 ++++++++++++++ 5 files changed, 129 insertions(+) diff --git a/mlir/include/mlir/Dialect/LLVMIR/FunctionCallUtils.h b/mlir/include/mlir/Dialect/LLVMIR/FunctionCallUtils.h index 7efff9774cd50..6380ff2d8e132 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/FunctionCallUtils.h +++ b/mlir/include/mlir/Dialect/LLVMIR/FunctionCallUtils.h @@ -45,6 +45,8 @@ LLVM::LLVMFuncOp lookupOrCreateMallocFn(ModuleOp moduleOp, Type indexType); LLVM::LLVMFuncOp lookupOrCreateAlignedAllocFn(ModuleOp moduleOp, Type indexType); LLVM::LLVMFuncOp lookupOrCreateFreeFn(ModuleOp moduleOp); +LLVM::LLVMFuncOp lookupOrCreateMemRefCopyFn(ModuleOp moduleOp, Type indexType, + Type unrankedDescriptorType); /// Create a FuncOp with signature `resultType`(`paramTypes`)` and name `name`. LLVM::LLVMFuncOp lookupOrCreateFn(ModuleOp moduleOp, StringRef name, diff --git a/mlir/include/mlir/ExecutionEngine/CRunnerUtils.h b/mlir/include/mlir/ExecutionEngine/CRunnerUtils.h index fb0b2a65a67eb..bd855fcc03a96 100644 --- a/mlir/include/mlir/ExecutionEngine/CRunnerUtils.h +++ b/mlir/include/mlir/ExecutionEngine/CRunnerUtils.h @@ -330,6 +330,13 @@ class DynamicMemRefType { const int64_t *strides; }; +//===----------------------------------------------------------------------===// +// Small runtime support library for memref.copy lowering during codegen. +//===----------------------------------------------------------------------===// +extern "C" MLIR_CRUNNERUTILS_EXPORT void +memrefCopy(int64_t elemSize, UnrankedMemRefType *src, + UnrankedMemRefType *dst); + //===----------------------------------------------------------------------===// // Small runtime support library for vector.print lowering during codegen. //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp b/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp index db5918e95f182..eb390bf8844fa 100644 --- a/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp +++ b/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp @@ -2618,6 +2618,68 @@ struct MemRefCastOpLowering : public ConvertOpToLLVMPattern { } }; +struct MemRefCopyOpLowering : public ConvertOpToLLVMPattern { + using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern; + + LogicalResult + matchAndRewrite(memref::CopyOp op, ArrayRef operands, + ConversionPatternRewriter &rewriter) const override { + auto loc = op.getLoc(); + memref::CopyOp::Adaptor adaptor(operands); + auto srcType = op.source().getType().cast(); + auto targetType = op.target().getType().cast(); + + // First make sure we have an unranked memref descriptor representation. + auto makeUnranked = [&, this](Value ranked, BaseMemRefType type) { + auto rank = rewriter.create( + loc, getIndexType(), rewriter.getIndexAttr(type.getRank())); + auto *typeConverter = getTypeConverter(); + auto ptr = + typeConverter->promoteOneMemRefDescriptor(loc, ranked, rewriter); + auto voidPtr = + rewriter.create(loc, getVoidPtrType(), ptr) + .getResult(); + auto unrankedType = + UnrankedMemRefType::get(type.getElementType(), type.getMemorySpace()); + return UnrankedMemRefDescriptor::pack(rewriter, loc, *typeConverter, + unrankedType, + ValueRange{rank, voidPtr}); + }; + + Value unrankedSource = srcType.hasRank() + ? makeUnranked(adaptor.source(), srcType) + : adaptor.source(); + Value unrankedTarget = targetType.hasRank() + ? makeUnranked(adaptor.target(), targetType) + : adaptor.target(); + + // Now promote the unranked descriptors to the stack. + auto one = rewriter.create(loc, getIndexType(), + rewriter.getIndexAttr(1)); + auto promote = [&](Value desc) { + auto ptrType = LLVM::LLVMPointerType::get(desc.getType()); + auto allocated = + rewriter.create(loc, ptrType, ValueRange{one}); + rewriter.create(loc, desc, allocated); + return allocated; + }; + + auto sourcePtr = promote(unrankedSource); + auto targetPtr = promote(unrankedTarget); + + auto elemSize = rewriter.create( + loc, getIndexType(), + rewriter.getIndexAttr(srcType.getElementTypeBitWidth() / 8)); + auto copyFn = LLVM::lookupOrCreateMemRefCopyFn( + op->getParentOfType(), getIndexType(), sourcePtr.getType()); + rewriter.create(loc, copyFn, + ValueRange{elemSize, sourcePtr, targetPtr}); + rewriter.eraseOp(op); + + return success(); + } +}; + /// Extracts allocated, aligned pointers and offset from a ranked or unranked /// memref type. In unranked case, the fields are extracted from the underlying /// ranked descriptor. @@ -4009,6 +4071,7 @@ void mlir::populateStdToLLVMMemoryConversionPatterns( GetGlobalMemrefOpLowering, LoadOpLowering, MemRefCastOpLowering, + MemRefCopyOpLowering, MemRefReinterpretCastOpLowering, MemRefReshapeOpLowering, RankOpLowering, diff --git a/mlir/lib/Dialect/LLVMIR/IR/FunctionCallUtils.cpp b/mlir/lib/Dialect/LLVMIR/IR/FunctionCallUtils.cpp index a43c2251c2d99..47a5851b51f2e 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/FunctionCallUtils.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/FunctionCallUtils.cpp @@ -35,6 +35,7 @@ static constexpr llvm::StringRef kPrintNewline = "printNewline"; static constexpr llvm::StringRef kMalloc = "malloc"; static constexpr llvm::StringRef kAlignedAlloc = "aligned_alloc"; static constexpr llvm::StringRef kFree = "free"; +static constexpr llvm::StringRef kMemRefCopy = "memref_copy"; /// Generic print function lookupOrCreate helper. LLVM::LLVMFuncOp mlir::LLVM::lookupOrCreateFn(ModuleOp moduleOp, StringRef name, @@ -114,6 +115,15 @@ LLVM::LLVMFuncOp mlir::LLVM::lookupOrCreateFreeFn(ModuleOp moduleOp) { LLVM::LLVMVoidType::get(moduleOp->getContext())); } +LLVM::LLVMFuncOp +mlir::LLVM::lookupOrCreateMemRefCopyFn(ModuleOp moduleOp, Type indexType, + Type unrankedDescriptorType) { + return LLVM::lookupOrCreateFn( + moduleOp, kMemRefCopy, + ArrayRef{indexType, unrankedDescriptorType, unrankedDescriptorType}, + LLVM::LLVMVoidType::get(moduleOp->getContext())); +} + Operation::result_range mlir::LLVM::createLLVMCall(OpBuilder &b, Location loc, LLVM::LLVMFuncOp fn, ValueRange paramTypes, diff --git a/mlir/lib/ExecutionEngine/CRunnerUtils.cpp b/mlir/lib/ExecutionEngine/CRunnerUtils.cpp index 4677098d64f3b..d4ebc46aa47da 100644 --- a/mlir/lib/ExecutionEngine/CRunnerUtils.cpp +++ b/mlir/lib/ExecutionEngine/CRunnerUtils.cpp @@ -23,6 +23,7 @@ #include #include +#include #ifdef MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS @@ -39,6 +40,52 @@ extern "C" void printClose() { fputs(" )", stdout); } extern "C" void printComma() { fputs(", ", stdout); } extern "C" void printNewline() { fputc('\n', stdout); } +extern "C" MLIR_CRUNNERUTILS_EXPORT void +memrefCopy(int64_t elemSize, UnrankedMemRefType *srcArg, + UnrankedMemRefType *dstArg) { + DynamicMemRefType src(*srcArg); + DynamicMemRefType dst(*dstArg); + + int64_t rank = src.rank; + int64_t *indices = static_cast(alloca(sizeof(int64_t) * rank)); + int64_t *srcStrides = static_cast(alloca(sizeof(int64_t) * rank)); + int64_t *dstStrides = static_cast(alloca(sizeof(int64_t) * rank)); + + char *srcPtr = src.data + src.offset * elemSize; + char *dstPtr = dst.data + dst.offset * elemSize; + + // Initialize index and scale strides. + for (int rankp = 0; rankp < rank; ++rankp) { + indices[rankp] = 0; + srcStrides[rankp] = src.strides[rankp] * elemSize; + dstStrides[rankp] = dst.strides[rankp] * elemSize; + } + + int64_t readIndex = 0, writeIndex = 0; + for (;;) { + // Copy over the element, byte by byte. + memcpy(dstPtr + writeIndex, srcPtr + readIndex, elemSize); + // Advance index and read position. + for (int64_t axis = rank - 1; axis >= 0; --axis) { + // Advance at current axis. + auto newIndex = ++indices[axis]; + readIndex += srcStrides[axis]; + writeIndex += dstStrides[axis]; + // If this is a valid index, we have our next index, so continue copying. + if (src.sizes[axis] != newIndex) + break; + // We reached the end of this axis. If this is axis 0, we are done. + if (axis == 0) + return; + // Else, reset to 0 and undo the advancement of the linear index that + // this axis had. The continue with the axis one outer. + indices[axis] = 0; + readIndex -= src.sizes[axis] * srcStrides[axis]; + writeIndex -= dst.sizes[axis] * dstStrides[axis]; + } + } +} + /// Prints GFLOPS rating. extern "C" void print_flops(double flops) { fprintf(stderr, "%lf GFLOPS\n", flops / 1.0E9); From a8d1182f661ccecd99efd4e543fddf3172c67a95 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Tue, 15 Jun 2021 12:47:05 -0400 Subject: [PATCH 05/24] [libc++] Remove some _LIBCPP_CXX03_LANG from iostreams headers. With the STL containers, I didn't enable move operations in C++03 mode because that would change the overload resolution for things that today are copy operations. With iostreams, though, the copy operations aren't present at all, and so I see no problem with enabling move operations even in (Clang's greatly extended) C++03 mode. Clang's C++03 mode does not support delegating constructors. Differential Revision: https://reviews.llvm.org/D104310 --- libcxx/include/fstream | 29 +------------ libcxx/include/ios | 2 - libcxx/include/istream | 19 +-------- libcxx/include/ostream | 14 +------ libcxx/include/sstream | 42 ++++--------------- libcxx/include/string | 4 -- .../filebuf.assign/move_assign.pass.cpp | 2 - .../fstreams/filebuf.cons/move.pass.cpp | 6 +-- .../fstream.assign/move_assign.pass.cpp | 2 - .../fstreams/fstream.cons/move.pass.cpp | 6 +-- .../ifstream.assign/move_assign.pass.cpp | 1 - .../fstreams/ifstream.cons/move.pass.cpp | 5 +-- .../ofstream.assign/move_assign.pass.cpp | 2 - .../fstreams/ofstream.cons/move.pass.cpp | 6 +-- .../iostream.assign/move_assign.pass.cpp | 2 - .../iostreamclass/iostream.cons/move.pass.cpp | 2 - .../istream.assign/move_assign.pass.cpp | 2 - .../istream/istream.cons/copy.fail.cpp | 5 --- .../istream/istream.cons/move.pass.cpp | 2 - .../ostream.assign/move_assign.pass.cpp | 2 - .../output.streams/ostream.cons/move.pass.cpp | 2 - .../is_error_code_enum_io_errc.pass.cpp | 2 - .../istringstream.assign/move.pass.cpp | 2 - .../istringstream.cons/move.pass.cpp | 2 - .../ostringstream.assign/move.pass.cpp | 2 - .../ostringstream.cons/move.pass.cpp | 2 - .../stringbuf/stringbuf.cons/move.pass.cpp | 12 +++--- .../stringstream.cons/move.pass.cpp | 2 - .../stringstream.assign/move.pass.cpp | 2 - .../string.io/get_line_delim_rv.pass.cpp | 6 +-- .../string.io/get_line_rv.pass.cpp | 6 +-- 31 files changed, 31 insertions(+), 164 deletions(-) diff --git a/libcxx/include/fstream b/libcxx/include/fstream index 09af0d91e79b6..c522b8ab110d7 100644 --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -219,16 +219,12 @@ public: // 27.9.1.2 Constructors/destructor: basic_filebuf(); -#ifndef _LIBCPP_CXX03_LANG basic_filebuf(basic_filebuf&& __rhs); -#endif virtual ~basic_filebuf(); // 27.9.1.3 Assign/swap: -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY basic_filebuf& operator=(basic_filebuf&& __rhs); -#endif void swap(basic_filebuf& __rhs); // 27.9.1.4 Members: @@ -318,8 +314,6 @@ basic_filebuf<_CharT, _Traits>::basic_filebuf() setbuf(nullptr, 4096); } -#ifndef _LIBCPP_CXX03_LANG - template basic_filebuf<_CharT, _Traits>::basic_filebuf(basic_filebuf&& __rhs) : basic_streambuf<_CharT, _Traits>(__rhs) @@ -394,8 +388,6 @@ basic_filebuf<_CharT, _Traits>::operator=(basic_filebuf&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template basic_filebuf<_CharT, _Traits>::~basic_filebuf() { @@ -1164,13 +1156,10 @@ public: : basic_ifstream(__p.c_str(), __mode) {} #endif // _LIBCPP_STD_VER >= 17 #endif -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY basic_ifstream(basic_ifstream&& __rhs); - _LIBCPP_INLINE_VISIBILITY basic_ifstream& operator=(basic_ifstream&& __rhs); -#endif _LIBCPP_INLINE_VISIBILITY void swap(basic_ifstream& __rhs); @@ -1240,8 +1229,6 @@ basic_ifstream<_CharT, _Traits>::basic_ifstream(const string& __s, ios_base::ope } #endif -#ifndef _LIBCPP_CXX03_LANG - template inline basic_ifstream<_CharT, _Traits>::basic_ifstream(basic_ifstream&& __rhs) @@ -1261,8 +1248,6 @@ basic_ifstream<_CharT, _Traits>::operator=(basic_ifstream&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template inline void @@ -1379,13 +1364,10 @@ public: : basic_ofstream(__p.c_str(), __mode) {} #endif // _LIBCPP_STD_VER >= 17 -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY basic_ofstream(basic_ofstream&& __rhs); - _LIBCPP_INLINE_VISIBILITY basic_ofstream& operator=(basic_ofstream&& __rhs); -#endif _LIBCPP_INLINE_VISIBILITY void swap(basic_ofstream& __rhs); @@ -1454,8 +1436,6 @@ basic_ofstream<_CharT, _Traits>::basic_ofstream(const string& __s, ios_base::ope } #endif -#ifndef _LIBCPP_CXX03_LANG - template inline basic_ofstream<_CharT, _Traits>::basic_ofstream(basic_ofstream&& __rhs) @@ -1475,8 +1455,6 @@ basic_ofstream<_CharT, _Traits>::operator=(basic_ofstream&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template inline void @@ -1595,13 +1573,12 @@ public: #endif // _LIBCPP_STD_VER >= 17 #endif -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY basic_fstream(basic_fstream&& __rhs); _LIBCPP_INLINE_VISIBILITY basic_fstream& operator=(basic_fstream&& __rhs); -#endif + _LIBCPP_INLINE_VISIBILITY void swap(basic_fstream& __rhs); @@ -1668,8 +1645,6 @@ basic_fstream<_CharT, _Traits>::basic_fstream(const string& __s, ios_base::openm } #endif -#ifndef _LIBCPP_CXX03_LANG - template inline basic_fstream<_CharT, _Traits>::basic_fstream(basic_fstream&& __rhs) @@ -1689,8 +1664,6 @@ basic_fstream<_CharT, _Traits>::operator=(basic_fstream&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template inline void diff --git a/libcxx/include/ios b/libcxx/include/ios index eefb58f55be13..3128bca899990 100644 --- a/libcxx/include/ios +++ b/libcxx/include/ios @@ -662,10 +662,8 @@ protected: _LIBCPP_INLINE_VISIBILITY void move(basic_ios& __rhs); -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY void move(basic_ios&& __rhs) {move(__rhs);} -#endif _LIBCPP_INLINE_VISIBILITY void swap(basic_ios& __rhs) _NOEXCEPT; _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/include/istream b/libcxx/include/istream index 531280719b30e..17ca68388f523 100644 --- a/libcxx/include/istream +++ b/libcxx/include/istream @@ -192,14 +192,12 @@ public: { this->init(__sb); } virtual ~basic_istream(); protected: -#ifndef _LIBCPP_CXX03_LANG inline _LIBCPP_INLINE_VISIBILITY basic_istream(basic_istream&& __rhs); // 27.7.1.1.2 Assign/swap: inline _LIBCPP_INLINE_VISIBILITY basic_istream& operator=(basic_istream&& __rhs); -#endif inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 void swap(basic_istream& __rhs) { @@ -207,10 +205,8 @@ protected: basic_ios::swap(__rhs); } -#ifndef _LIBCPP_CXX03_LANG basic_istream (const basic_istream& __rhs) = delete; basic_istream& operator=(const basic_istream& __rhs) = delete; -#endif public: // 27.7.1.1.3 Prefix/suffix: @@ -333,8 +329,6 @@ basic_istream<_CharT, _Traits>::sentry::sentry(basic_istream<_CharT, _Traits>& _ __is.setstate(ios_base::failbit); } -#ifndef _LIBCPP_CXX03_LANG - template basic_istream<_CharT, _Traits>::basic_istream(basic_istream&& __rhs) : __gc_(__rhs.__gc_) @@ -351,8 +345,6 @@ basic_istream<_CharT, _Traits>::operator=(basic_istream&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template basic_istream<_CharT, _Traits>::~basic_istream() { @@ -1416,21 +1408,18 @@ public: virtual ~basic_iostream(); protected: -#ifndef _LIBCPP_CXX03_LANG inline _LIBCPP_INLINE_VISIBILITY basic_iostream(basic_iostream&& __rhs); // assign/swap inline _LIBCPP_INLINE_VISIBILITY basic_iostream& operator=(basic_iostream&& __rhs); -#endif + inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 void swap(basic_iostream& __rhs) { basic_istream::swap(__rhs); } }; -#ifndef _LIBCPP_CXX03_LANG - template basic_iostream<_CharT, _Traits>::basic_iostream(basic_iostream&& __rhs) : basic_istream<_CharT, _Traits>(_VSTD::move(__rhs)) @@ -1445,8 +1434,6 @@ basic_iostream<_CharT, _Traits>::operator=(basic_iostream&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template basic_iostream<_CharT, _Traits>::~basic_iostream() { @@ -1570,8 +1557,6 @@ getline(basic_istream<_CharT, _Traits>& __is, return getline(__is, __str, __is.widen('\n')); } -#ifndef _LIBCPP_CXX03_LANG - template inline _LIBCPP_INLINE_VISIBILITY basic_istream<_CharT, _Traits>& @@ -1590,8 +1575,6 @@ getline(basic_istream<_CharT, _Traits>&& __is, return getline(__is, __str, __is.widen('\n')); } -#endif // _LIBCPP_CXX03_LANG - template basic_istream<_CharT, _Traits>& operator>>(basic_istream<_CharT, _Traits>& __is, bitset<_Size>& __x) diff --git a/libcxx/include/ostream b/libcxx/include/ostream index 81ba565e67f53..efeaee253eb97 100644 --- a/libcxx/include/ostream +++ b/libcxx/include/ostream @@ -165,27 +165,21 @@ public: { this->init(__sb); } virtual ~basic_ostream(); protected: -#ifndef _LIBCPP_CXX03_LANG inline _LIBCPP_INLINE_VISIBILITY basic_ostream(basic_ostream&& __rhs); // 27.7.2.3 Assign/swap inline _LIBCPP_INLINE_VISIBILITY basic_ostream& operator=(basic_ostream&& __rhs); -#endif + inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 void swap(basic_ostream& __rhs) { basic_ios::swap(__rhs); } -#ifndef _LIBCPP_CXX03_LANG basic_ostream (const basic_ostream& __rhs) = delete; basic_ostream& operator=(const basic_ostream& __rhs) = delete; -#else - basic_ostream (const basic_ostream& __rhs); // not defined - basic_ostream& operator=(const basic_ostream& __rhs); // not defined -#endif -public: +public: // 27.7.2.4 Prefix/suffix: class _LIBCPP_TEMPLATE_VIS sentry; @@ -291,8 +285,6 @@ basic_ostream<_CharT, _Traits>::sentry::~sentry() } } -#ifndef _LIBCPP_CXX03_LANG - template basic_ostream<_CharT, _Traits>::basic_ostream(basic_ostream&& __rhs) { @@ -307,8 +299,6 @@ basic_ostream<_CharT, _Traits>::operator=(basic_ostream&& __rhs) return *this; } -#endif // _LIBCPP_CXX03_LANG - template basic_ostream<_CharT, _Traits>::~basic_ostream() { diff --git a/libcxx/include/sstream b/libcxx/include/sstream index 0b614a0b09562..fbe5ffcab4c6e 100644 --- a/libcxx/include/sstream +++ b/libcxx/include/sstream @@ -219,19 +219,13 @@ private: public: // 30.8.2.1 [stringbuf.cons], constructors -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY - basic_stringbuf() : basic_stringbuf(ios_base::in | ios_base::out) {} + basic_stringbuf() + : __hm_(nullptr), __mode_(ios_base::in | ios_base::out) {} _LIBCPP_INLINE_VISIBILITY explicit basic_stringbuf(ios_base::openmode __wch) : __hm_(nullptr), __mode_(__wch) {} -#else - _LIBCPP_INLINE_VISIBILITY - explicit basic_stringbuf(ios_base::openmode __wch = ios_base::in | - ios_base::out) - : __hm_(nullptr), __mode_(__wch) {} -#endif _LIBCPP_INLINE_VISIBILITY explicit basic_stringbuf(const string_type& __s, @@ -643,18 +637,13 @@ private: public: // 30.8.3.1 [istringstream.cons], constructors -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY - basic_istringstream() : basic_istringstream(ios_base::in) {} + basic_istringstream() + : basic_istream<_CharT, _Traits>(&__sb_), __sb_(ios_base::in) {} _LIBCPP_INLINE_VISIBILITY explicit basic_istringstream(ios_base::openmode __wch) : basic_istream<_CharT, _Traits>(&__sb_), __sb_(__wch | ios_base::in) {} -#else - _LIBCPP_INLINE_VISIBILITY - explicit basic_istringstream(ios_base::openmode __wch = ios_base::in) - : basic_istream<_CharT, _Traits>(&__sb_), __sb_(__wch | ios_base::in) {} -#endif _LIBCPP_INLINE_VISIBILITY explicit basic_istringstream(const string_type& __s, @@ -728,20 +717,13 @@ private: public: // 30.8.4.1 [ostringstream.cons], constructors -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY - basic_ostringstream() : basic_ostringstream(ios_base::out) {} + basic_ostringstream() + : basic_ostream<_CharT, _Traits>(&__sb_), __sb_(ios_base::out) {} _LIBCPP_INLINE_VISIBILITY explicit basic_ostringstream(ios_base::openmode __wch) - : basic_ostream<_CharT, _Traits>(&__sb_), - __sb_(__wch | ios_base::out) {} -#else - _LIBCPP_INLINE_VISIBILITY - explicit basic_ostringstream(ios_base::openmode __wch = ios_base::out) - : basic_ostream<_CharT, _Traits>(&__sb_), - __sb_(__wch | ios_base::out) {} -#endif + : basic_ostream<_CharT, _Traits>(&__sb_), __sb_(__wch | ios_base::out) {} _LIBCPP_INLINE_VISIBILITY explicit basic_ostringstream(const string_type& __s, @@ -816,19 +798,13 @@ private: public: // 30.8.5.1 [stringstream.cons], constructors -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY - basic_stringstream() : basic_stringstream(ios_base::in | ios_base::out) {} + basic_stringstream() + : basic_iostream<_CharT, _Traits>(&__sb_), __sb_(ios_base::in | ios_base::out) {} _LIBCPP_INLINE_VISIBILITY explicit basic_stringstream(ios_base::openmode __wch) : basic_iostream<_CharT, _Traits>(&__sb_), __sb_(__wch) {} -#else - _LIBCPP_INLINE_VISIBILITY - explicit basic_stringstream(ios_base::openmode __wch = ios_base::in | - ios_base::out) - : basic_iostream<_CharT, _Traits>(&__sb_), __sb_(__wch) {} -#endif _LIBCPP_INLINE_VISIBILITY explicit basic_stringstream(const string_type& __s, diff --git a/libcxx/include/string b/libcxx/include/string index c5e0745250ee4..3917c07a1f744 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -4439,8 +4439,6 @@ basic_istream<_CharT, _Traits>& getline(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _Allocator>& __str); -#ifndef _LIBCPP_CXX03_LANG - template inline _LIBCPP_INLINE_VISIBILITY basic_istream<_CharT, _Traits>& @@ -4453,8 +4451,6 @@ basic_istream<_CharT, _Traits>& getline(basic_istream<_CharT, _Traits>&& __is, basic_string<_CharT, _Traits, _Allocator>& __str); -#endif // _LIBCPP_CXX03_LANG - #if _LIBCPP_STD_VER > 17 template inline _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/move_assign.pass.cpp index 69ccd202564db..173c9d110ffc4 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/move_assign.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.cons/move.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.cons/move.pass.cpp index 0a541670858e0..38aba56540f0d 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > @@ -31,7 +29,7 @@ int main(int, char**) assert(f.sputn("123", 3) == 3); f.pubseekoff(1, std::ios_base::beg); assert(f.sgetc() == '2'); - std::filebuf f2(move(f)); + std::filebuf f2(std::move(f)); assert(!f.is_open()); assert(f2.is_open()); assert(f2.sgetc() == '2'); @@ -45,7 +43,7 @@ int main(int, char**) assert(f.sputn(L"123", 3) == 3); f.pubseekoff(1, std::ios_base::beg); assert(f.sgetc() == L'2'); - std::wfilebuf f2(move(f)); + std::wfilebuf f2(std::move(f)); assert(!f.is_open()); assert(f2.is_open()); assert(f2.sgetc() == L'2'); diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.assign/move_assign.pass.cpp index 6032b21c579c3..9bc7887f839fd 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.assign/move_assign.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/move.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/move.pass.cpp index 9166737543a0e..e210b562ac949 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > @@ -26,7 +24,7 @@ int main(int, char**) { std::fstream fso(temp, std::ios_base::in | std::ios_base::out | std::ios_base::trunc); - std::fstream fs = move(fso); + std::fstream fs = std::move(fso); double x = 0; fs << 3.25; fs.seekg(0); @@ -37,7 +35,7 @@ int main(int, char**) { std::wfstream fso(temp, std::ios_base::in | std::ios_base::out | std::ios_base::trunc); - std::wfstream fs = move(fso); + std::wfstream fs = std::move(fso); double x = 0; fs << 3.25; fs.seekg(0); diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.assign/move_assign.pass.cpp index b9df31149efdc..439db87a0eba3 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.assign/move_assign.pass.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 // FILE_DEPENDENCIES: test.dat // diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.cons/move.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.cons/move.pass.cpp index 03475e17beef0..28f55f6a857db 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.cons/move.pass.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 // FILE_DEPENDENCIES: test.dat // @@ -25,14 +24,14 @@ int main(int, char**) { { std::ifstream fso("test.dat"); - std::ifstream fs = move(fso); + std::ifstream fs = std::move(fso); double x = 0; fs >> x; assert(x == 3.25); } { std::wifstream fso("test.dat"); - std::wifstream fs = move(fso); + std::wifstream fs = std::move(fso); double x = 0; fs >> x; assert(x == 3.25); diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.assign/move_assign.pass.cpp index 720bcf0e77486..4db1e6c3b35fb 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.assign/move_assign.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/move.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/move.pass.cpp index 60fbf2b9e360e..01676f0e75a0d 100644 --- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > @@ -25,7 +23,7 @@ int main(int, char**) std::string temp = get_temp_file_name(); { std::ofstream fso(temp.c_str()); - std::ofstream fs = move(fso); + std::ofstream fs = std::move(fso); fs << 3.25; } { @@ -37,7 +35,7 @@ int main(int, char**) std::remove(temp.c_str()); { std::wofstream fso(temp.c_str()); - std::wofstream fs = move(fso); + std::wofstream fs = std::move(fso); fs << 3.25; } { diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.assign/move_assign.pass.cpp index 7b02842610d5b..fb1f524c7123e 100644 --- a/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.assign/move_assign.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.cons/move.pass.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.cons/move.pass.cpp index 514cde055875a..2a4e1a6b1123b 100644 --- a/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/input.streams/iostreamclass/iostream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.assign/move_assign.pass.cpp index bd901171cc8ee..7899c63e8293e 100644 --- a/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.assign/move_assign.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/copy.fail.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/copy.fail.cpp index c5f10fa0145c2..90e5315a662b3 100644 --- a/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/copy.fail.cpp +++ b/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/copy.fail.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > @@ -47,10 +45,7 @@ struct test_istream }; - int main(int, char**) { - - return 0; } diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/move.pass.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/move.pass.cpp index 22163156c2c82..fb071ba970163 100644 --- a/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/input.streams/istream/istream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.assign/move_assign.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.assign/move_assign.pass.cpp index b529970a2883c..8fcb0c2b1ee97 100644 --- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.assign/move_assign.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.assign/move_assign.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.cons/move.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.cons/move.pass.cpp index 345388baf6e94..73991b36f6c9b 100644 --- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template > diff --git a/libcxx/test/std/input.output/iostreams.base/is_error_code_enum_io_errc.pass.cpp b/libcxx/test/std/input.output/iostreams.base/is_error_code_enum_io_errc.pass.cpp index 76eb83148b398..13f90d92899ed 100644 --- a/libcxx/test/std/input.output/iostreams.base/is_error_code_enum_io_errc.pass.cpp +++ b/libcxx/test/std/input.output/iostreams.base/is_error_code_enum_io_errc.pass.cpp @@ -5,8 +5,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// UNSUPPORTED: c++03 // diff --git a/libcxx/test/std/input.output/string.streams/istringstream/istringstream.assign/move.pass.cpp b/libcxx/test/std/input.output/string.streams/istringstream/istringstream.assign/move.pass.cpp index c9b701397d761..93c8313d85909 100644 --- a/libcxx/test/std/input.output/string.streams/istringstream/istringstream.assign/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/istringstream/istringstream.assign/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template , class Allocator = allocator > diff --git a/libcxx/test/std/input.output/string.streams/istringstream/istringstream.cons/move.pass.cpp b/libcxx/test/std/input.output/string.streams/istringstream/istringstream.cons/move.pass.cpp index e4f1f2b4ddbf3..650284d87b706 100644 --- a/libcxx/test/std/input.output/string.streams/istringstream/istringstream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/istringstream/istringstream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template , class Allocator = allocator > diff --git a/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.assign/move.pass.cpp b/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.assign/move.pass.cpp index 52e4ace45efff..8be3a43fe38db 100644 --- a/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.assign/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.assign/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template , class Allocator = allocator > diff --git a/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.cons/move.pass.cpp b/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.cons/move.pass.cpp index 3a3f42f009c20..6823bb69bc627 100644 --- a/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/ostringstream/ostringstream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template , class Allocator = allocator > diff --git a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/move.pass.cpp b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/move.pass.cpp index 9fb588d1a4304..af1eb38c349b4 100644 --- a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/move.pass.cpp @@ -22,32 +22,32 @@ int main(int, char**) { { std::stringbuf buf1("testing"); - std::stringbuf buf(move(buf1)); + std::stringbuf buf(std::move(buf1)); assert(buf.str() == "testing"); } { std::stringbuf buf1("testing", std::ios_base::in); - std::stringbuf buf(move(buf1)); + std::stringbuf buf(std::move(buf1)); assert(buf.str() == "testing"); } { std::stringbuf buf1("testing", std::ios_base::out); - std::stringbuf buf(move(buf1)); + std::stringbuf buf(std::move(buf1)); assert(buf.str() == "testing"); } { std::wstringbuf buf1(L"testing"); - std::wstringbuf buf(move(buf1)); + std::wstringbuf buf(std::move(buf1)); assert(buf.str() == L"testing"); } { std::wstringbuf buf1(L"testing", std::ios_base::in); - std::wstringbuf buf(move(buf1)); + std::wstringbuf buf(std::move(buf1)); assert(buf.str() == L"testing"); } { std::wstringbuf buf1(L"testing", std::ios_base::out); - std::wstringbuf buf(move(buf1)); + std::wstringbuf buf(std::move(buf1)); assert(buf.str() == L"testing"); } diff --git a/libcxx/test/std/input.output/string.streams/stringstream.cons/move.pass.cpp b/libcxx/test/std/input.output/string.streams/stringstream.cons/move.pass.cpp index 71c385a068060..642fd752a8f46 100644 --- a/libcxx/test/std/input.output/string.streams/stringstream.cons/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/stringstream.cons/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template , class Allocator = allocator > diff --git a/libcxx/test/std/input.output/string.streams/stringstream.cons/stringstream.assign/move.pass.cpp b/libcxx/test/std/input.output/string.streams/stringstream.cons/stringstream.assign/move.pass.cpp index e2a750651c6eb..91a685d2db9f7 100644 --- a/libcxx/test/std/input.output/string.streams/stringstream.cons/stringstream.assign/move.pass.cpp +++ b/libcxx/test/std/input.output/string.streams/stringstream.cons/stringstream.assign/move.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template , class Allocator = allocator > diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_delim_rv.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_delim_rv.pass.cpp index af2e62a5c808b..d2e5ab5488bdd 100644 --- a/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_delim_rv.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_delim_rv.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template @@ -35,13 +33,13 @@ int main(int, char**) assert(s == L" abc"); } { - typedef std::basic_string, min_allocator> S; + typedef std::basic_string, min_allocator > S; S s("initial text"); getline(std::istringstream(" abc* def* ghij"), s, '*'); assert(s == " abc"); } { - typedef std::basic_string, min_allocator> S; + typedef std::basic_string, min_allocator > S; S s(L"initial text"); getline(std::wistringstream(L" abc* def* ghij"), s, L'*'); assert(s == L" abc"); diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_rv.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_rv.pass.cpp index 322b49b9c6e7e..3fb35cc24d66b 100644 --- a/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_rv.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string.io/get_line_rv.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 - // // template @@ -35,13 +33,13 @@ int main(int, char**) assert(s == L" abc"); } { - typedef std::basic_string, min_allocator> S; + typedef std::basic_string, min_allocator > S; S s("initial text"); getline(std::istringstream(" abc\n def\n ghij"), s); assert(s == " abc"); } { - typedef std::basic_string, min_allocator> S; + typedef std::basic_string, min_allocator > S; S s(L"initial text"); getline(std::wistringstream(L" abc\n def\n ghij"), s); assert(s == L" abc"); From 5d6240b77e7e7199fcf0e89f6dd2f7eea3596a3c Mon Sep 17 00:00:00 2001 From: "William S. Moses" Date: Fri, 25 Jun 2021 19:40:35 -0400 Subject: [PATCH 06/24] [MLIR][SCF] Inline ExecuteRegion if parent can contain multiple blocks The executeregionop is used to allow multiple blocks within SCF constructs. If the container allows multiple blocks, inline the region Differential Revision: https://reviews.llvm.org/D104960 --- mlir/include/mlir/Dialect/SCF/SCFOps.td | 6 -- mlir/lib/Dialect/SCF/SCF.cpp | 77 ++++++++++++++++++++++++- mlir/test/Dialect/SCF/canonicalize.mlir | 67 +++++++++++++++++++++ 3 files changed, 141 insertions(+), 9 deletions(-) diff --git a/mlir/include/mlir/Dialect/SCF/SCFOps.td b/mlir/include/mlir/Dialect/SCF/SCFOps.td index c10441f59bd55..9f039b6fcda68 100644 --- a/mlir/include/mlir/Dialect/SCF/SCFOps.td +++ b/mlir/include/mlir/Dialect/SCF/SCFOps.td @@ -108,14 +108,8 @@ def ExecuteRegionOp : SCF_Op<"execute_region"> { let regions = (region AnyRegion:$region); - // TODO: If the parent is a func like op (which would be the case if all other - // ops are from the std dialect), the inliner logic could be readily used to - // inline. let hasCanonicalizer = 1; - // TODO: can fold if it returns a constant. - // TODO: Single block execute_region ops can be readily inlined irrespective - // of which op is a parent. Add a fold for this. let hasFolder = 0; } diff --git a/mlir/lib/Dialect/SCF/SCF.cpp b/mlir/lib/Dialect/SCF/SCF.cpp index 99d2386ced1b1..38760ca4050d3 100644 --- a/mlir/lib/Dialect/SCF/SCF.cpp +++ b/mlir/lib/Dialect/SCF/SCF.cpp @@ -143,23 +143,94 @@ static LogicalResult verify(ExecuteRegionOp op) { // // "test.foo"() : () -> () // %x = "test.val"() : () -> i64 -// "test.bar"(%v) : (i64) -> () +// "test.bar"(%x) : (i64) -> () // struct SingleBlockExecuteInliner : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(ExecuteRegionOp op, PatternRewriter &rewriter) const override { - if (op.region().getBlocks().size() != 1) + if (!llvm::hasSingleElement(op.region())) return failure(); replaceOpWithRegion(rewriter, op, op.region()); return success(); } }; +// Inline an ExecuteRegionOp if its parent can contain multiple blocks. +// TODO generalize the conditions for operations which can be inlined into. +// func @func_execute_region_elim() { +// "test.foo"() : () -> () +// %v = scf.execute_region -> i64 { +// %c = "test.cmp"() : () -> i1 +// cond_br %c, ^bb2, ^bb3 +// ^bb2: +// %x = "test.val1"() : () -> i64 +// br ^bb4(%x : i64) +// ^bb3: +// %y = "test.val2"() : () -> i64 +// br ^bb4(%y : i64) +// ^bb4(%z : i64): +// scf.yield %z : i64 +// } +// "test.bar"(%v) : (i64) -> () +// return +// } +// +// becomes +// +// func @func_execute_region_elim() { +// "test.foo"() : () -> () +// %c = "test.cmp"() : () -> i1 +// cond_br %c, ^bb1, ^bb2 +// ^bb1: // pred: ^bb0 +// %x = "test.val1"() : () -> i64 +// br ^bb3(%x : i64) +// ^bb2: // pred: ^bb0 +// %y = "test.val2"() : () -> i64 +// br ^bb3(%y : i64) +// ^bb3(%z: i64): // 2 preds: ^bb1, ^bb2 +// "test.bar"(%z) : (i64) -> () +// return +// } +// +struct MultiBlockExecuteInliner : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(ExecuteRegionOp op, + PatternRewriter &rewriter) const override { + if (!isa(op->getParentOp())) + return failure(); + + Block *prevBlock = op->getBlock(); + Block *postBlock = rewriter.splitBlock(prevBlock, op->getIterator()); + rewriter.setInsertionPointToEnd(prevBlock); + + rewriter.create(op.getLoc(), &op.region().front()); + + for (Block &blk : op.region()) { + if (YieldOp yieldOp = dyn_cast(blk.getTerminator())) { + rewriter.setInsertionPoint(yieldOp); + rewriter.create(yieldOp.getLoc(), postBlock, + yieldOp.results()); + rewriter.eraseOp(yieldOp); + } + } + + rewriter.inlineRegionBefore(op.region(), postBlock); + SmallVector blockArgs; + + for (auto res : op.getResults()) + blockArgs.push_back(postBlock->addArgument(res.getType())); + + rewriter.replaceOp(op, blockArgs); + return success(); + } +}; + void ExecuteRegionOp::getCanonicalizationPatterns(RewritePatternSet &results, MLIRContext *context) { - results.add(context); + results.add(context); } //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/SCF/canonicalize.mlir b/mlir/test/Dialect/SCF/canonicalize.mlir index 8692f2d9705e0..d1789c6dfde52 100644 --- a/mlir/test/Dialect/SCF/canonicalize.mlir +++ b/mlir/test/Dialect/SCF/canonicalize.mlir @@ -948,3 +948,70 @@ func @execute_region_elim() { // CHECK-NEXT: "test.bar"(%[[VAL]]) : (i64) -> () // CHECK-NEXT: } + +// ----- + +// CHECK-LABEL: func @func_execute_region_elim +func @func_execute_region_elim() { + "test.foo"() : () -> () + %v = scf.execute_region -> i64 { + %c = "test.cmp"() : () -> i1 + cond_br %c, ^bb2, ^bb3 + ^bb2: + %x = "test.val1"() : () -> i64 + br ^bb4(%x : i64) + ^bb3: + %y = "test.val2"() : () -> i64 + br ^bb4(%y : i64) + ^bb4(%z : i64): + scf.yield %z : i64 + } + "test.bar"(%v) : (i64) -> () + return +} + +// CHECK: "test.foo" +// CHECK: %[[cmp:.+]] = "test.cmp" +// CHECK: cond_br %[[cmp]], ^[[bb1:.+]], ^[[bb2:.+]] +// CHECK: ^[[bb1]]: // pred: ^bb0 +// CHECK: %[[x:.+]] = "test.val1" +// CHECK: br ^[[bb3:.+]](%[[x]] : i64) +// CHECK: ^[[bb2]]: // pred: ^bb0 +// CHECK: %[[y:.+]] = "test.val2" +// CHECK: br ^[[bb3]](%[[y:.+]] : i64) +// CHECK: ^[[bb3]](%[[z:.+]]: i64): +// CHECK: "test.bar"(%[[z]]) +// CHECK: return + + +// ----- + +// CHECK-LABEL: func @func_execute_region_elim2 +func @func_execute_region_elim2() { + "test.foo"() : () -> () + %v = scf.execute_region -> i64 { + %c = "test.cmp"() : () -> i1 + cond_br %c, ^bb2, ^bb3 + ^bb2: + %x = "test.val1"() : () -> i64 + scf.yield %x : i64 + ^bb3: + %y = "test.val2"() : () -> i64 + scf.yield %y : i64 + } + "test.bar"(%v) : (i64) -> () + return +} + +// CHECK: "test.foo" +// CHECK: %[[cmp:.+]] = "test.cmp" +// CHECK: cond_br %[[cmp]], ^[[bb1:.+]], ^[[bb2:.+]] +// CHECK: ^[[bb1]]: // pred: ^bb0 +// CHECK: %[[x:.+]] = "test.val1" +// CHECK: br ^[[bb3:.+]](%[[x]] : i64) +// CHECK: ^[[bb2]]: // pred: ^bb0 +// CHECK: %[[y:.+]] = "test.val2" +// CHECK: br ^[[bb3]](%[[y:.+]] : i64) +// CHECK: ^[[bb3]](%[[z:.+]]: i64): +// CHECK: "test.bar"(%[[z]]) +// CHECK: return From 9c5ed8d567924e807a6466b6ad681c8bf395cf58 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Fri, 25 Jun 2021 17:45:50 -0500 Subject: [PATCH 07/24] [Hexagon] Add patterns to load i1 This fixes https://llvm.org/PR50853 --- llvm/lib/Target/Hexagon/HexagonPatterns.td | 62 ++- llvm/test/CodeGen/Hexagon/isel-extload-i1.ll | 25 ++ llvm/test/CodeGen/Hexagon/isel/extload-i1.ll | 380 +++++++++++++++++++ 3 files changed, 451 insertions(+), 16 deletions(-) create mode 100644 llvm/test/CodeGen/Hexagon/isel-extload-i1.ll create mode 100644 llvm/test/CodeGen/Hexagon/isel/extload-i1.ll diff --git a/llvm/lib/Target/Hexagon/HexagonPatterns.td b/llvm/lib/Target/Hexagon/HexagonPatterns.td index f4223b74c9008..cad5ca8ab92ec 100644 --- a/llvm/lib/Target/Hexagon/HexagonPatterns.td +++ b/llvm/lib/Target/Hexagon/HexagonPatterns.td @@ -1948,6 +1948,9 @@ def: Pat<(HexagonAtPcrel I32:$addr), // --(12) Load ----------------------------------------------------------- // +def L1toI32: OutPatFrag<(ops node:$Rs), (A2_subri 0, (i32 $Rs))>; +def L1toI64: OutPatFrag<(ops node:$Rs), (ToSext64 (L1toI32 $Rs))>; + def extloadv2i8: PatFrag<(ops node:$ptr), (extload node:$ptr), [{ return cast(N)->getMemoryVT() == MVT::v2i8; }]>; @@ -2104,11 +2107,17 @@ let AddedComplexity = 20 in { } let AddedComplexity = 30 in { + // Loads of i1 are loading a byte, and the byte should be either 0 or 1. + // It doesn't matter if it's sign- or zero-extended, so use zero-extension + // everywhere. + defm: Loadxim_pat; defm: Loadxim_pat; + defm: Loadxim_pat; + defm: Loadxim_pat; + defm: Loadxim_pat; defm: Loadxim_pat; defm: Loadxim_pat; - defm: Loadxim_pat; defm: Loadxim_pat; defm: Loadxim_pat; defm: Loadxim_pat; @@ -2118,6 +2127,7 @@ let AddedComplexity = 30 in { } let AddedComplexity = 60 in { + def: Loadxu_pat; def: Loadxu_pat; def: Loadxu_pat; def: Loadxu_pat; @@ -2126,6 +2136,7 @@ let AddedComplexity = 60 in { def: Loadxu_pat; def: Loadxu_pat; def: Loadxu_pat; + def: Loadxu_pat; def: Loadxu_pat; def: Loadxu_pat; def: Loadxu_pat; @@ -2140,6 +2151,11 @@ let AddedComplexity = 60 in { def: Loadxu_pat; def: Loadxu_pat; + def: Loadxum_pat; + def: Loadxum_pat; + def: Loadxum_pat; + def: Loadxum_pat; + def: Loadxum_pat; def: Loadxum_pat; def: Loadxum_pat; @@ -2152,7 +2168,9 @@ let AddedComplexity = 60 in { } let AddedComplexity = 40 in { + def: Loadxr_shl_pat; def: Loadxr_shl_pat; + def: Loadxr_shl_pat; def: Loadxr_shl_pat; def: Loadxr_shl_pat; def: Loadxr_shl_pat; @@ -2170,8 +2188,10 @@ let AddedComplexity = 40 in { } let AddedComplexity = 20 in { + def: Loadxr_add_pat; def: Loadxr_add_pat; def: Loadxr_add_pat; + def: Loadxr_add_pat; def: Loadxr_add_pat; def: Loadxr_add_pat; def: Loadxr_add_pat; @@ -2188,6 +2208,11 @@ let AddedComplexity = 20 in { } let AddedComplexity = 40 in { + def: Loadxrm_shl_pat; + def: Loadxrm_shl_pat; + def: Loadxrm_shl_pat; + def: Loadxrm_shl_pat; + def: Loadxrm_shl_pat; def: Loadxrm_shl_pat; def: Loadxrm_shl_pat; @@ -2199,7 +2224,12 @@ let AddedComplexity = 40 in { def: Loadxrm_shl_pat; } -let AddedComplexity = 20 in { +let AddedComplexity = 30 in { + def: Loadxrm_add_pat; + def: Loadxrm_add_pat; + def: Loadxrm_add_pat; + def: Loadxrm_add_pat; + def: Loadxrm_add_pat; def: Loadxrm_add_pat; def: Loadxrm_add_pat; @@ -2214,12 +2244,13 @@ let AddedComplexity = 20 in { // Absolute address let AddedComplexity = 60 in { + def: Loada_pat; def: Loada_pat; - def: Loada_pat; def: Loada_pat; + def: Loada_pat; def: Loada_pat; - def: Loada_pat; def: Loada_pat; + def: Loada_pat; def: Loada_pat; def: Loada_pat; def: Loada_pat; @@ -2238,6 +2269,12 @@ let AddedComplexity = 60 in { } let AddedComplexity = 30 in { + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; def: Loadam_pat; def: Loadam_pat; @@ -2247,9 +2284,6 @@ let AddedComplexity = 30 in { def: Loadam_pat; def: Loadam_pat; def: Loadam_pat; - - def: Loadam_pat; - def: Loadam_pat; } // GP-relative address @@ -2280,6 +2314,11 @@ let AddedComplexity = 100 in { } let AddedComplexity = 70 in { + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; + def: Loadam_pat; def: Loadam_pat; def: Loadam_pat; @@ -2291,17 +2330,8 @@ let AddedComplexity = 70 in { def: Loadam_pat; def: Loadam_pat; - def: Loadam_pat; } - -// Sign-extending loads of i1 need to replicate the lowest bit throughout -// the 32-bit value. Since the loaded value can only be 0 or 1, 0-v should -// do the trick. -let AddedComplexity = 20 in -def: Pat<(i32 (sextloadi1 I32:$Rs)), - (A2_subri 0, (L2_loadrub_io IntRegs:$Rs, 0))>; - // Patterns for loads of i1: def: Pat<(i1 (load AddrFI:$fi)), (C2_tfrrp (L2_loadrub_io AddrFI:$fi, 0))>; diff --git a/llvm/test/CodeGen/Hexagon/isel-extload-i1.ll b/llvm/test/CodeGen/Hexagon/isel-extload-i1.ll new file mode 100644 index 0000000000000..7c3f73d098476 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/isel-extload-i1.ll @@ -0,0 +1,25 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -march=hexagon < %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048" +target triple = "hexagon" + +define i64 @f0(i32 %a0, i64 %a1, i32 %a2, i32 %a3, i1 zeroext %a4) #0 { +; CHECK-LABEL: f0: +; CHECK: // %bb.0: // %b0 +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r29+#0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } +b0: + %v0 = sext i1 %a4 to i64 + ret i64 %v0 +} + +attributes #0 = { nounwind "target-cpu"="hexagonv66" "target-features"="+v66,-long-calls" } diff --git a/llvm/test/CodeGen/Hexagon/isel/extload-i1.ll b/llvm/test/CodeGen/Hexagon/isel/extload-i1.ll new file mode 100644 index 0000000000000..def04ee4026c1 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/isel/extload-i1.ll @@ -0,0 +1,380 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -march=hexagon < %s | FileCheck %s + +@array8 = global [128 x i8] zeroinitializer +@array32 = global [128 x i32] zeroinitializer +@global_gp = global i1 false + +; Sign extensions + +define i32 @f0(i1* %a0) #0 { +; CHECK-LABEL: f0: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+#1) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 1 + %v1 = load i1, i1* %v0 + %v2 = sext i1 %v1 to i32 + ret i32 %v2 +} + +define i32 @f1(i1* %a0, i32 %a1) #0 { +; CHECK-LABEL: f1: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+r1<<#0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 %a1 + %v1 = load i1, i1* %v0 + %v2 = sext i1 %v1 to i32 + ret i32 %v2 +} + +define i32 @f2(i32 %a0) #0 { +; CHECK-LABEL: f2: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+##array8) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr [128 x i8], [128 x i8]* @array8, i32 0, i32 %a0 + %v1 = bitcast i8* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = sext i1 %v2 to i32 + ret i32 %v3 +} + +define i32 @f3(i32 %a0) #0 { +; CHECK-LABEL: f3: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0<<#2+##array32) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr [128 x i32], [128 x i32]* @array32, i32 0, i32 %a0 + %v1 = bitcast i32* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = sext i1 %v2 to i32 + ret i32 %v3 +} + +define i32 @f4() #0 { +; CHECK-LABEL: f4: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(gp+#global_gp) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = load i1, i1* @global_gp + %v1 = sext i1 %v0 to i32 + ret i32 %v1 +} + +define i32 @f5(i64 %a0, i64 %a1, i64 %a2, i1 signext %a3) #0 { +; CHECK-LABEL: f5: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r29+#0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = sext i1 %a3 to i32 + ret i32 %v0 +} + +define i64 @f6(i1* %a0) #0 { +; CHECK-LABEL: f6: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+#1) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 1 + %v1 = load i1, i1* %v0 + %v2 = sext i1 %v1 to i64 + ret i64 %v2 +} + +define i64 @f7(i1* %a0, i32 %a1) #0 { +; CHECK-LABEL: f7: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+r1<<#0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 %a1 + %v1 = load i1, i1* %v0 + %v2 = sext i1 %v1 to i64 + ret i64 %v2 +} + +define i64 @f8(i32 %a0) #0 { +; CHECK-LABEL: f8: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+##array8) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr [128 x i8], [128 x i8]* @array8, i32 0, i32 %a0 + %v1 = bitcast i8* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = sext i1 %v2 to i64 + ret i64 %v3 +} + +define i64 @f9(i32 %a0) #0 { +; CHECK-LABEL: f9: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0<<#2+##array32) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr [128 x i32], [128 x i32]* @array32, i32 0, i32 %a0 + %v1 = bitcast i32* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = sext i1 %v2 to i64 + ret i64 %v3 +} + +define i64 @f10() #0 { +; CHECK-LABEL: f10: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(gp+#global_gp) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = load i1, i1* @global_gp + %v1 = sext i1 %v0 to i64 + ret i64 %v1 +} + +define i64 @f11(i64 %a0, i64 %a1, i64 %a2, i1 signext %a3) #0 { +; CHECK-LABEL: f11: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r29+#0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = sub(#0,r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1 = asr(r0,#31) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = sext i1 %a3 to i64 + ret i64 %v0 +} + +; Zero-extensions + +define i32 @f12(i1* %a0) #0 { +; CHECK-LABEL: f12: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = memub(r0+#1) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 1 + %v1 = load i1, i1* %v0 + %v2 = zext i1 %v1 to i32 + ret i32 %v2 +} + +define i32 @f13(i1* %a0, i32 %a1) #0 { +; CHECK-LABEL: f13: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r0+r1<<#0) +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 %a1 + %v1 = load i1, i1* %v0 + %v2 = zext i1 %v1 to i32 + ret i32 %v2 +} + +define i32 @f14(i32 %a0) #0 { +; CHECK-LABEL: f14: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r0+##array8) +; CHECK-NEXT: } + %v0 = getelementptr [128 x i8], [128 x i8]* @array8, i32 0, i32 %a0 + %v1 = bitcast i8* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = zext i1 %v2 to i32 + ret i32 %v3 +} + +define i32 @f15(i32 %a0) #0 { +; CHECK-LABEL: f15: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r0<<#2+##array32) +; CHECK-NEXT: } + %v0 = getelementptr [128 x i32], [128 x i32]* @array32, i32 0, i32 %a0 + %v1 = bitcast i32* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = zext i1 %v2 to i32 + ret i32 %v3 +} + +define i32 @f16() #0 { +; CHECK-LABEL: f16: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(gp+#global_gp) +; CHECK-NEXT: } + %v0 = load i1, i1* @global_gp + %v1 = zext i1 %v0 to i32 + ret i32 %v1 +} + +define i32 @f17(i64 %a0, i64 %a1, i64 %a2, i1 zeroext %a3) #0 { +; CHECK-LABEL: f17: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r29+#0) +; CHECK-NEXT: } + %v0 = zext i1 %a3 to i32 + ret i32 %v0 +} + +define i64 @f18(i1* %a0) #0 { +; CHECK-LABEL: f18: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: r0 = memub(r0+#1) +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 1 + %v1 = load i1, i1* %v0 + %v2 = zext i1 %v1 to i64 + ret i64 %v2 +} + +define i64 @f19(i1* %a0, i32 %a1) #0 { +; CHECK-LABEL: f19: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r0+r1<<#0) +; CHECK-NEXT: } + %v0 = getelementptr i1, i1* %a0, i32 %a1 + %v1 = load i1, i1* %v0 + %v2 = zext i1 %v1 to i64 + ret i64 %v2 +} + +define i64 @f20(i32 %a0) #0 { +; CHECK-LABEL: f20: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r0+##array8) +; CHECK-NEXT: } + %v0 = getelementptr [128 x i8], [128 x i8]* @array8, i32 0, i32 %a0 + %v1 = bitcast i8* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = zext i1 %v2 to i64 + ret i64 %v3 +} + +define i64 @f21(i32 %a0) #0 { +; CHECK-LABEL: f21: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r0<<#2+##array32) +; CHECK-NEXT: } + %v0 = getelementptr [128 x i32], [128 x i32]* @array32, i32 0, i32 %a0 + %v1 = bitcast i32* %v0 to i1* + %v2 = load i1, i1* %v1 + %v3 = zext i1 %v2 to i64 + ret i64 %v3 +} + +define i64 @f22() #0 { +; CHECK-LABEL: f22: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(gp+#global_gp) +; CHECK-NEXT: } + %v0 = load i1, i1* @global_gp + %v1 = zext i1 %v0 to i64 + ret i64 %v1 +} + +define i64 @f23(i64 %a0, i64 %a1, i64 %a2, i1 signext %a3) #0 { +; CHECK-LABEL: f23: +; CHECK: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r0 = memub(r29+#0) +; CHECK-NEXT: } + %v0 = zext i1 %a3 to i64 + ret i64 %v0 +} + +attributes #0 = { nounwind "target-cpu"="hexagonv66" } From c7676d9993183f7041b1d79cc672ff14961c8777 Mon Sep 17 00:00:00 2001 From: Rob Suderman Date: Mon, 28 Jun 2021 10:17:32 -0700 Subject: [PATCH 08/24] [mlir][tosa] Update Tosa conv verifier to handle IntegerType input Input/output types can be integers, which represent a quantized convolution. Update verifier to expect this behavior. Reviewed By: sjarus Differential Revision: https://reviews.llvm.org/D104949 --- mlir/lib/Dialect/Tosa/IR/TosaOps.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp index e50dab12aaf14..83a89f3af80d6 100644 --- a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp +++ b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp @@ -121,19 +121,20 @@ static LogicalResult verifyConvOp(T op) { if (!inputType || !weightType) return failure(); - auto inputQType = - inputType.getElementType().template isa(); - auto weightQType = - weightType.getElementType().template isa(); + auto inputEType = inputType.getElementType(); + auto weightEType = weightType.getElementType(); + + bool inputIsQuant = !inputEType.template isa(); + bool weightIsQuant = !weightEType.template isa(); // Either both must be quantized or both unquantized. - if (inputQType != weightQType) + if (inputIsQuant != weightIsQuant) return failure(); // Quantized type must have constructed the quantizationattr, and unquantized // types should not have a quantizationattr. - if ((inputQType && !op.quantization_info()) || - (!inputQType && op.quantization_info())) + if ((inputIsQuant && !op.quantization_info()) || + (!inputIsQuant && op.quantization_info())) return failure(); return success(); From a4aa705d52e818cf526f5e41cce8e719befd97a6 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Mon, 28 Jun 2021 10:38:18 -0700 Subject: [PATCH 09/24] [lldb] Remove spurious lldb/lldb subdirectory Remove the lldb/lldb subdirectory which I must have accidentally created when applying a patch with the wrong prefix number. Thank you Nico Weber for pointing this out! --- lldb/lldb/test/Shell/Breakpoint/breakpoint-command.test | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 lldb/lldb/test/Shell/Breakpoint/breakpoint-command.test diff --git a/lldb/lldb/test/Shell/Breakpoint/breakpoint-command.test b/lldb/lldb/test/Shell/Breakpoint/breakpoint-command.test deleted file mode 100644 index 6104713cde5ae..0000000000000 --- a/lldb/lldb/test/Shell/Breakpoint/breakpoint-command.test +++ /dev/null @@ -1,5 +0,0 @@ -# RUN: %build %p/Inputs/dummy-target.c -o %t.out -# RUN: %lldb %t.out -o 'b main' -o 'break command add 1 -o "script print(95000 + 126)"' -o 'r' - -# CHECK: 95125 -# CHECK-NOT: 95126 From 18c3c7784975700ae463bb461487d46e74324a66 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 28 Jun 2021 13:45:12 -0400 Subject: [PATCH 10/24] Add papers adopted by the C++ committee in the June 2021 plenary --- clang/www/cxx_status.html | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index b7f2501cbc534..8de688189e297 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1283,6 +1283,41 @@

C++2b implementation status

P2266R1 Clang 13 + + if consteval + P1938R3 + No + + + Allow duplicate attributes + P2156R1 + Clang 13 + + + Narrowing contextual conversions to bool + P1401R5 + No + + + Trimming whitespaces before line splicing + P2223R2 + Yes + + + Make declaration order layout mandated + P1874R4 + Yes + + + C++ identifier syntax using UAX 31 + P1949R7 + No + + + Mixed string literal concatenation + P2201R1 + Yes + From 2ab27758d5c5e7985cee1a2651bc0a9ee4c2d8c9 Mon Sep 17 00:00:00 2001 From: "William S. Moses" Date: Mon, 28 Jun 2021 13:52:30 -0400 Subject: [PATCH 11/24] Revert "[MLIR][SCF] Inline ExecuteRegion if parent can contain multiple blocks" This reverts commit 5d6240b77e7e7199fcf0e89f6dd2f7eea3596a3c. The commit was mistakenly landed without a PR approval, this will be reverted now and resubmitted. --- mlir/include/mlir/Dialect/SCF/SCFOps.td | 6 ++ mlir/lib/Dialect/SCF/SCF.cpp | 77 +------------------------ mlir/test/Dialect/SCF/canonicalize.mlir | 67 --------------------- 3 files changed, 9 insertions(+), 141 deletions(-) diff --git a/mlir/include/mlir/Dialect/SCF/SCFOps.td b/mlir/include/mlir/Dialect/SCF/SCFOps.td index 9f039b6fcda68..c10441f59bd55 100644 --- a/mlir/include/mlir/Dialect/SCF/SCFOps.td +++ b/mlir/include/mlir/Dialect/SCF/SCFOps.td @@ -108,8 +108,14 @@ def ExecuteRegionOp : SCF_Op<"execute_region"> { let regions = (region AnyRegion:$region); + // TODO: If the parent is a func like op (which would be the case if all other + // ops are from the std dialect), the inliner logic could be readily used to + // inline. let hasCanonicalizer = 1; + // TODO: can fold if it returns a constant. + // TODO: Single block execute_region ops can be readily inlined irrespective + // of which op is a parent. Add a fold for this. let hasFolder = 0; } diff --git a/mlir/lib/Dialect/SCF/SCF.cpp b/mlir/lib/Dialect/SCF/SCF.cpp index 38760ca4050d3..99d2386ced1b1 100644 --- a/mlir/lib/Dialect/SCF/SCF.cpp +++ b/mlir/lib/Dialect/SCF/SCF.cpp @@ -143,94 +143,23 @@ static LogicalResult verify(ExecuteRegionOp op) { // // "test.foo"() : () -> () // %x = "test.val"() : () -> i64 -// "test.bar"(%x) : (i64) -> () +// "test.bar"(%v) : (i64) -> () // struct SingleBlockExecuteInliner : public OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(ExecuteRegionOp op, PatternRewriter &rewriter) const override { - if (!llvm::hasSingleElement(op.region())) + if (op.region().getBlocks().size() != 1) return failure(); replaceOpWithRegion(rewriter, op, op.region()); return success(); } }; -// Inline an ExecuteRegionOp if its parent can contain multiple blocks. -// TODO generalize the conditions for operations which can be inlined into. -// func @func_execute_region_elim() { -// "test.foo"() : () -> () -// %v = scf.execute_region -> i64 { -// %c = "test.cmp"() : () -> i1 -// cond_br %c, ^bb2, ^bb3 -// ^bb2: -// %x = "test.val1"() : () -> i64 -// br ^bb4(%x : i64) -// ^bb3: -// %y = "test.val2"() : () -> i64 -// br ^bb4(%y : i64) -// ^bb4(%z : i64): -// scf.yield %z : i64 -// } -// "test.bar"(%v) : (i64) -> () -// return -// } -// -// becomes -// -// func @func_execute_region_elim() { -// "test.foo"() : () -> () -// %c = "test.cmp"() : () -> i1 -// cond_br %c, ^bb1, ^bb2 -// ^bb1: // pred: ^bb0 -// %x = "test.val1"() : () -> i64 -// br ^bb3(%x : i64) -// ^bb2: // pred: ^bb0 -// %y = "test.val2"() : () -> i64 -// br ^bb3(%y : i64) -// ^bb3(%z: i64): // 2 preds: ^bb1, ^bb2 -// "test.bar"(%z) : (i64) -> () -// return -// } -// -struct MultiBlockExecuteInliner : public OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(ExecuteRegionOp op, - PatternRewriter &rewriter) const override { - if (!isa(op->getParentOp())) - return failure(); - - Block *prevBlock = op->getBlock(); - Block *postBlock = rewriter.splitBlock(prevBlock, op->getIterator()); - rewriter.setInsertionPointToEnd(prevBlock); - - rewriter.create(op.getLoc(), &op.region().front()); - - for (Block &blk : op.region()) { - if (YieldOp yieldOp = dyn_cast(blk.getTerminator())) { - rewriter.setInsertionPoint(yieldOp); - rewriter.create(yieldOp.getLoc(), postBlock, - yieldOp.results()); - rewriter.eraseOp(yieldOp); - } - } - - rewriter.inlineRegionBefore(op.region(), postBlock); - SmallVector blockArgs; - - for (auto res : op.getResults()) - blockArgs.push_back(postBlock->addArgument(res.getType())); - - rewriter.replaceOp(op, blockArgs); - return success(); - } -}; - void ExecuteRegionOp::getCanonicalizationPatterns(RewritePatternSet &results, MLIRContext *context) { - results.add(context); + results.add(context); } //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/SCF/canonicalize.mlir b/mlir/test/Dialect/SCF/canonicalize.mlir index d1789c6dfde52..8692f2d9705e0 100644 --- a/mlir/test/Dialect/SCF/canonicalize.mlir +++ b/mlir/test/Dialect/SCF/canonicalize.mlir @@ -948,70 +948,3 @@ func @execute_region_elim() { // CHECK-NEXT: "test.bar"(%[[VAL]]) : (i64) -> () // CHECK-NEXT: } - -// ----- - -// CHECK-LABEL: func @func_execute_region_elim -func @func_execute_region_elim() { - "test.foo"() : () -> () - %v = scf.execute_region -> i64 { - %c = "test.cmp"() : () -> i1 - cond_br %c, ^bb2, ^bb3 - ^bb2: - %x = "test.val1"() : () -> i64 - br ^bb4(%x : i64) - ^bb3: - %y = "test.val2"() : () -> i64 - br ^bb4(%y : i64) - ^bb4(%z : i64): - scf.yield %z : i64 - } - "test.bar"(%v) : (i64) -> () - return -} - -// CHECK: "test.foo" -// CHECK: %[[cmp:.+]] = "test.cmp" -// CHECK: cond_br %[[cmp]], ^[[bb1:.+]], ^[[bb2:.+]] -// CHECK: ^[[bb1]]: // pred: ^bb0 -// CHECK: %[[x:.+]] = "test.val1" -// CHECK: br ^[[bb3:.+]](%[[x]] : i64) -// CHECK: ^[[bb2]]: // pred: ^bb0 -// CHECK: %[[y:.+]] = "test.val2" -// CHECK: br ^[[bb3]](%[[y:.+]] : i64) -// CHECK: ^[[bb3]](%[[z:.+]]: i64): -// CHECK: "test.bar"(%[[z]]) -// CHECK: return - - -// ----- - -// CHECK-LABEL: func @func_execute_region_elim2 -func @func_execute_region_elim2() { - "test.foo"() : () -> () - %v = scf.execute_region -> i64 { - %c = "test.cmp"() : () -> i1 - cond_br %c, ^bb2, ^bb3 - ^bb2: - %x = "test.val1"() : () -> i64 - scf.yield %x : i64 - ^bb3: - %y = "test.val2"() : () -> i64 - scf.yield %y : i64 - } - "test.bar"(%v) : (i64) -> () - return -} - -// CHECK: "test.foo" -// CHECK: %[[cmp:.+]] = "test.cmp" -// CHECK: cond_br %[[cmp]], ^[[bb1:.+]], ^[[bb2:.+]] -// CHECK: ^[[bb1]]: // pred: ^bb0 -// CHECK: %[[x:.+]] = "test.val1" -// CHECK: br ^[[bb3:.+]](%[[x]] : i64) -// CHECK: ^[[bb2]]: // pred: ^bb0 -// CHECK: %[[y:.+]] = "test.val2" -// CHECK: br ^[[bb3]](%[[y:.+]] : i64) -// CHECK: ^[[bb3]](%[[z:.+]]: i64): -// CHECK: "test.bar"(%[[z]]) -// CHECK: return From 355541a1b7a5011f8f4ebadc3e23b25c734f9d27 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Mon, 28 Jun 2021 18:58:42 +0200 Subject: [PATCH 12/24] [lldb] Avoid using any shell when calling xcrun. When we run `xcrun` we don't have any user input in our command so relying on the user's default shell doesn't make a lot of sense. If the user has set the system shell to a something that isn't supported yet (dash, ash) then we would run into the problem that we don't know how to escape our command string. This patch just avoids using any shell at all as xcrun is always at the same path. Reviewed By: aprantl, JDevlieghere, kastiglione Differential Revision: https://reviews.llvm.org/D104653 --- .../source/Host/macosx/objcxx/HostInfoMacOSX.mm | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm index f822533f1b41a..a0706ec9ff6ae 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -383,17 +383,22 @@ static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { auto xcrun = [](const std::string &sdk, llvm::StringRef developer_dir = "") -> std::string { - std::string xcrun_cmd = "xcrun --show-sdk-path --sdk " + sdk; - if (!developer_dir.empty()) - xcrun_cmd = "/usr/bin/env DEVELOPER_DIR=\"" + developer_dir.str() + - "\" " + xcrun_cmd; + Args args; + if (!developer_dir.empty()) { + args.AppendArgument("/usr/bin/env"); + args.AppendArgument("DEVELOPER_DIR=" + developer_dir.str()); + } + args.AppendArgument("/usr/bin/xcrun"); + args.AppendArgument("--show-sdk-path"); + args.AppendArgument("--sdk"); + args.AppendArgument(sdk); int status = 0; int signo = 0; std::string output_str; lldb_private::Status error = - Host::RunShellCommand(xcrun_cmd, FileSpec(), &status, &signo, - &output_str, std::chrono::seconds(15)); + Host::RunShellCommand(args, FileSpec(), &status, &signo, &output_str, + std::chrono::seconds(15)); // Check that xcrun return something useful. if (status != 0 || output_str.empty()) From 2a60ab76a796637d49bf1c7191f5b5a0c92f81bc Mon Sep 17 00:00:00 2001 From: Florian Mayer Date: Mon, 28 Jun 2021 11:34:30 +0100 Subject: [PATCH 13/24] [hwasan] print exact mismatch offset for short granules. Reviewed By: eugenis Differential Revision: https://reviews.llvm.org/D104463 --- compiler-rt/lib/hwasan/hwasan_report.cpp | 19 +++++++++++++-- .../TestCases/heap-buffer-overflow-into.c | 23 +++++++++++++++---- .../hwasan/TestCases/heap-buffer-overflow.c | 2 ++ .../test/hwasan/TestCases/mem-intrinsics.c | 2 +- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/compiler-rt/lib/hwasan/hwasan_report.cpp b/compiler-rt/lib/hwasan/hwasan_report.cpp index 715b4e05992a6..b6f968ea10457 100644 --- a/compiler-rt/lib/hwasan/hwasan_report.cpp +++ b/compiler-rt/lib/hwasan/hwasan_report.cpp @@ -630,9 +630,24 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size, Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n", is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag, mem_tag, t->unique_id()); + if (mem_tag < kShadowAlignment) { + tag_t *granule_ptr = reinterpret_cast((untagged_addr + offset) & + ~(kShadowAlignment - 1)); + // If offset is 0, (untagged_addr + offset) is not aligned to granules. + // This is the offset of the leftmost accessed byte within the bad granule. + u8 in_granule_offset = (untagged_addr + offset) & (kShadowAlignment - 1); + // The first mismatch was a short granule that matched the ptr_tag. + if (granule_ptr[kShadowAlignment - 1] == ptr_tag) { + // If the access starts after the end of the short granule, then the first + // bad byte is the first byte of the access; otherwise it is the first + // byte past the end of the short granule + if (mem_tag > in_granule_offset) { + offset += mem_tag - in_granule_offset; + } + } + } if (offset != 0) - Printf("Invalid access starting at offset [%zu, %zu)\n", offset, - Min(access_size, static_cast(offset) + (1 << kShadowScale))); + Printf("Invalid access starting at offset %zu\n", offset); Printf("%s", d.Default()); stack->Print(); diff --git a/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow-into.c b/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow-into.c index af4256b84db03..8526c81f4cd7d 100644 --- a/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow-into.c +++ b/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow-into.c @@ -1,5 +1,8 @@ // RUN: %clang_hwasan %s -o %t -// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: not %run %t 5 10 2>&1 | FileCheck %s --check-prefix=CHECK5 +// RUN: not %run %t 7 10 2>&1 | FileCheck %s --check-prefix=CHECK7 +// RUN: not %run %t 8 20 2>&1 | FileCheck %s --check-prefix=CHECK8 +// RUN: not %run %t 32 20 2>&1 | FileCheck %s --check-prefix=CHECK32 // REQUIRES: stable-runtime @@ -10,8 +13,20 @@ int main(int argc, char **argv) { __hwasan_enable_allocator_tagging(); - char *volatile x = (char *)malloc(10); - memset(x + 5, 0, 26); - // CHECK: is located 5 bytes inside 10-byte region + if (argc < 2) { + fprintf(stderr, "Invalid number of arguments."); + abort(); + } + int read_offset = argc < 2 ? 5 : atoi(argv[1]); + int size = argc < 3 ? 10 : atoi(argv[2]); + char *volatile x = (char *)malloc(size); + memset(x + read_offset, 0, 26); + // CHECK5: Invalid access starting at offset 5 + // CHECK5: is located 5 bytes inside 10-byte region + // CHECK7: Invalid access starting at offset 3 + // CHECK7: is located 7 bytes inside 10-byte region + // CHECK8: Invalid access starting at offset 12 + // CHECK8: is located 8 bytes inside 20-byte region + // CHECK32: is located 12 bytes to the right of 20-byte region free(x); } diff --git a/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c b/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c index 67398141209af..8e8719a7f65c4 100644 --- a/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c +++ b/compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c @@ -52,12 +52,14 @@ int main(int argc, char **argv) { // CHECKM: is located 0 bytes to the right of 1000000-byte region // // CHECK31: tags: [[TAG:..]]/0e (ptr/mem) +// CHECK31-NOT: Invalid access starting at offset // CHECK31: is located 1 bytes to the right of 30-byte region // CHECK31: Memory tags around the buggy address // CHECK31: [0e] // CHECK31: Tags for short granules around the buggy address // CHECK31: {{\[}}[[TAG]]] // +// CHECK20-NOT: Invalid access starting at offset // CHECK20: is located 10 bytes to the right of 20-byte region [0x{{.*}}0,0x{{.*}}4) free(x); } diff --git a/compiler-rt/test/hwasan/TestCases/mem-intrinsics.c b/compiler-rt/test/hwasan/TestCases/mem-intrinsics.c index 28568c828cea1..44b9fd67cbcc6 100644 --- a/compiler-rt/test/hwasan/TestCases/mem-intrinsics.c +++ b/compiler-rt/test/hwasan/TestCases/mem-intrinsics.c @@ -23,7 +23,7 @@ int main() { write(STDOUT_FILENO, "recovered\n", 10); // WRITE: ERROR: HWAddressSanitizer: tag-mismatch on address // WRITE: WRITE of size 32 at {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) - // WRITE: Invalid access starting at offset [16, 32) + // WRITE: Invalid access starting at offset 16 // WRITE: Memory tags around the buggy address (one tag corresponds to 16 bytes): // WRITE: =>{{.*}}[[PTR_TAG]]{{[[:space:]]\[}}[[MEM_TAG]] // WRITE-NOT: recovered From f85b9d644398767f6b5cb046f952ed7dbd7dfc7a Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 28 Jun 2021 11:02:30 -0700 Subject: [PATCH 14/24] [ObjC][ARC] Ignore operand bundle "clang.arc.attachedcall" on a call if the call's return type is void Instead of trying hard to prevent global optimization passes such as deadargelim from changing the return type to void, just ignore the bundle if the return type is void. clang currently emits calls to @llvm.objc.clang.arc.noop.use, which consumes the function call result, immediately after the function call to prevent changes to the return type, but optimization passes can delete the call to @llvm.objc.clang.arc.noop.use if the function call doesn't return, which enables deadargelim to change the return type. rdar://76671438 Differential Revision: https://reviews.llvm.org/D103062 --- llvm/docs/LangRef.rst | 6 ++++-- llvm/include/llvm/Analysis/ObjCARCUtil.h | 19 ++++++++++++++----- llvm/lib/IR/Verifier.cpp | 6 ++++-- .../Transforms/ObjCARC/contract-rv-attr.ll | 13 +++++++++++++ llvm/test/Verifier/operand-bundles.ll | 7 +++++++ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 1986f232cc3e3..083ece600448f 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2406,8 +2406,10 @@ A ``"clang.arc.attachedcall`` operand bundle on a call indicates the call is implicitly followed by a marker instruction and a call to an ObjC runtime function that uses the result of the call. If the argument passed to the operand bundle is 0, ``@objc_retainAutoreleasedReturnValue`` is called. If 1 is passed, -``@objc_unsafeClaimAutoreleasedReturnValue`` is called. A call with this bundle -implicitly uses its return value. +``@objc_unsafeClaimAutoreleasedReturnValue`` is called. The return value of a +call with this bundle is used by a call to ``@llvm.objc.clang.arc.noop.use`` +unless the called function's return type is void, in which case the operand +bundle is ignored. The operand bundle is needed to ensure the call is immediately followed by the marker instruction or the ObjC runtime call in the final output. diff --git a/llvm/include/llvm/Analysis/ObjCARCUtil.h b/llvm/include/llvm/Analysis/ObjCARCUtil.h index 5d04ebadf0851..2566bfbcf61cc 100644 --- a/llvm/include/llvm/Analysis/ObjCARCUtil.h +++ b/llvm/include/llvm/Analysis/ObjCARCUtil.h @@ -31,7 +31,21 @@ getAttachedCallOperandBundleEnum(bool IsRetain) { return IsRetain ? RVOB_Retain : RVOB_Claim; } +inline bool hasAttachedCallOpBundle(const CallBase *CB) { + // Ignore the bundle if the return type is void. Global optimization passes + // can turn the called function's return type to void. That should happen only + // if the call doesn't return and the call to @llvm.objc.clang.arc.noop.use + // no longer consumes the function return or is deleted. In that case, it's + // not necessary to emit the marker instruction or calls to the ARC runtime + // functions. + return !CB->getFunctionType()->getReturnType()->isVoidTy() && + CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall) + .hasValue(); +} + inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { + assert(hasAttachedCallOpBundle(CB) && + "call doesn't have operand bundle clang_arc_attachedcall"); auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); if (!B.hasValue()) return false; @@ -39,11 +53,6 @@ inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { getAttachedCallOperandBundleEnum(IsRetain); } -inline bool hasAttachedCallOpBundle(const CallBase *CB) { - return CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall) - .hasValue(); -} - } // end namespace objcarc } // end namespace llvm diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 468c935e3bbf2..24f5d51381803 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -3353,9 +3353,11 @@ void Verifier::visitCallBase(CallBase &Call) { } if (FoundAttachedCallBundle) - Assert(FTy->getReturnType()->isPointerTy(), + Assert((FTy->getReturnType()->isPointerTy() || + (Call.doesNotReturn() && FTy->getReturnType()->isVoidTy())), "a call with operand bundle \"clang.arc.attachedcall\" must call a " - "function returning a pointer", + "function returning a pointer or a non-returning function that has " + "a void return type", Call); // Verify that each inlinable callsite of a debug-info-bearing function in a diff --git a/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll b/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll index 3a817327c3638..18bc00b62db8d 100644 --- a/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll +++ b/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll @@ -55,9 +55,22 @@ cleanup: ret i8* %retval.0 } +; CHECK-LABEL: define void @test3( +; CHECK: call void @foo2() #[[ATTR1:.*]] [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: ret void + +define void @test3() { + call void @foo2() #0 [ "clang.arc.attachedcall"(i64 0) ] + ret void +} + declare i8* @foo() +declare void @foo2() declare i32 @__gxx_personality_v0(...) !llvm.module.flags = !{!0} +; CHECK: attributes #[[ATTR1]] = { noreturn } +attributes #0 = { noreturn } + !0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker", !"mov\09fp, fp\09\09// marker for objc_retainAutoreleaseReturnValue"} diff --git a/llvm/test/Verifier/operand-bundles.ll b/llvm/test/Verifier/operand-bundles.ll index 4ef0e647988af..d7d7b4f0f7820 100644 --- a/llvm/test/Verifier/operand-bundles.ll +++ b/llvm/test/Verifier/operand-bundles.ll @@ -4,6 +4,7 @@ declare void @g() declare %0* @foo0() declare i8 @foo1() +declare void @noreturn_func() ; Operand bundles uses are like regular uses, and need to be dominated ; by their defs. @@ -69,9 +70,15 @@ define void @f_clang_arc_attachedcall() { ; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i64 0), "clang.arc.attachedcall"(i64 0) ] ; CHECK-NEXT: must call a function returning a pointer ; CHECK-NEXT: call i8 @foo1() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: or a non-returning function +; CHECK-NEXT: call void @g() [ "clang.arc.attachedcall"(i64 0) ] call %0* @foo0() [ "clang.arc.attachedcall"(i64 0) ] call %0* @foo0() [ "clang.arc.attachedcall"(i64 0), "clang.arc.attachedcall"(i64 0) ] call i8 @foo1() [ "clang.arc.attachedcall"(i64 0) ] + call void @noreturn_func() #0 [ "clang.arc.attachedcall"(i64 0) ] + call void @g() [ "clang.arc.attachedcall"(i64 0) ] ret void } + +attributes #0 = { noreturn } From 4f5ebfdcd6c9d459e262d1815f49a45bad3cbcfc Mon Sep 17 00:00:00 2001 From: Nancy Wang Date: Mon, 28 Jun 2021 14:04:02 -0400 Subject: [PATCH 15/24] [SystemZ][z/OS][libcxx]: fix libcxx test cases failed on ebcdic mode on z/OS This patch is to fix 2 libcxx test cases, test cases assumed 'a' > 'A' which is not case in z/OS platform on ebcdic mode, modified test cases to compare between upper letters or lower letters, or digits so ordering will be true for all platform. Differential Revision: https://reviews.llvm.org/D104748 --- .../lt.pass.cpp | 18 +++++++++--------- .../lt.pass.cpp | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/lt.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/lt.pass.cpp index fa14e666e9e90..81a8574eac8bd 100644 --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/lt.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/lt.pass.cpp @@ -19,18 +19,18 @@ int main(int, char**) { - assert( std::char_traits::lt('\0', 'A')); - assert(!std::char_traits::lt('A', '\0')); + assert(std::char_traits::lt('\0', 'A') == ('\0' < 'A')); + assert(std::char_traits::lt('A', '\0') == ('A' < '\0')); - assert(!std::char_traits::lt('a', 'a')); - assert( std::char_traits::lt('A', 'a')); - assert(!std::char_traits::lt('a', 'A')); + assert(std::char_traits::lt('a', 'a') == ('a' < 'a')); + assert(std::char_traits::lt('A', 'a') == ('A' < 'a')); + assert(std::char_traits::lt('a', 'A') == ('a' < 'A')); - assert( std::char_traits::lt('a', 'z')); - assert( std::char_traits::lt('A', 'Z')); + assert(std::char_traits::lt('a', 'z') == ('a' < 'z')); + assert(std::char_traits::lt('A', 'Z') == ('A' < 'Z')); - assert( std::char_traits::lt(' ', 'A')); - assert( std::char_traits::lt('A', '~')); + assert(std::char_traits::lt(' ', 'A') == (' ' < 'A')); + assert(std::char_traits::lt('A', '~') == ('A' < '~')); return 0; } diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/lt.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/lt.pass.cpp index 15a16b0cd92da..5664692addb41 100644 --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/lt.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/lt.pass.cpp @@ -19,8 +19,18 @@ int main(int, char**) { - assert(!std::char_traits::lt(L'a', L'a')); - assert( std::char_traits::lt(L'A', L'a')); + assert(std::char_traits::lt(L'\0', L'A') == (L'\0' < L'A')); + assert(std::char_traits::lt(L'A', L'\0') == (L'A' < L'\0')); + + assert(std::char_traits::lt(L'a', L'a') == (L'a' < L'a')); + assert(std::char_traits::lt(L'A', L'a') == (L'A' < L'a')); + assert(std::char_traits::lt(L'a', L'A') == (L'a' < L'A')); + + assert(std::char_traits::lt(L'a', L'z') == (L'a' < L'z')); + assert(std::char_traits::lt(L'A', L'Z') == (L'A' < L'Z')); + + assert(std::char_traits::lt(L' ', L'A') == (L' ' < L'A')); + assert(std::char_traits::lt(L'A', L'~') == (L'A' < L'~')); return 0; } From 57e53f013087d68305fe278aca0a92efc9b0e899 Mon Sep 17 00:00:00 2001 From: Peter Steinfeld Date: Fri, 25 Jun 2021 11:28:30 -0700 Subject: [PATCH 16/24] [flang] Fix conformability for intrinsic procedures There are situations where the arguments of intrinsics must be conformable, which is defined in section 3.36. This means they must have "the same shape, or one being an array and the other being scalar". But the check we were actually making was that their ranks were the same. This change fixes that and adds a test for the UNPACK intrinsic, where the FIELD argument "shall be conformable with MASK". Differential Revision: https://reviews.llvm.org/D104936 --- flang/lib/Evaluate/intrinsics.cpp | 19 +++++++++++++++++-- flang/test/Semantics/unpack.f90 | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 flang/test/Semantics/unpack.f90 diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp index c8d8b02d58abc..5e305055b6913 100644 --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -1355,6 +1355,7 @@ std::optional IntrinsicInterface::Match( // Check the ranks of the arguments against the intrinsic's interface. const ActualArgument *arrayArg{nullptr}; + const char *arrayArgName{nullptr}; const ActualArgument *knownArg{nullptr}; std::optional shapeArgSize; int elementalRank{0}; @@ -1411,6 +1412,7 @@ std::optional IntrinsicInterface::Match( argOk = rank > 0; if (!arrayArg) { arrayArg = arg; + arrayArgName = d.keyword; } else { argOk &= rank == arrayArg->Rank(); } @@ -1424,9 +1426,22 @@ std::optional IntrinsicInterface::Match( case Rank::anyOrAssumedRank: argOk = true; break; - case Rank::conformable: + case Rank::conformable: // arg must be conformable with previous arrayArg CHECK(arrayArg); - argOk = rank == 0 || rank == arrayArg->Rank(); + CHECK(arrayArgName); + if (const std::optional &arrayArgShape{ + GetShape(context, *arrayArg)}) { + if (const std::optional &argShape{GetShape(context, *arg)}) { + std::string arrayArgMsg{"'"}; + arrayArgMsg = arrayArgMsg + arrayArgName + "='" + " argument"; + std::string argMsg{"'"}; + argMsg = argMsg + d.keyword + "='" + " argument"; + CheckConformance(context.messages(), *arrayArgShape, *argShape, + CheckConformanceFlags::RightScalarExpandable, + arrayArgMsg.c_str(), argMsg.c_str()); + } + } + argOk = true; // Avoid an additional error message break; case Rank::dimReduced: case Rank::dimRemovedOrScalar: diff --git a/flang/test/Semantics/unpack.f90 b/flang/test/Semantics/unpack.f90 new file mode 100644 index 0000000000000..d624f9c2e38a1 --- /dev/null +++ b/flang/test/Semantics/unpack.f90 @@ -0,0 +1,15 @@ +! RUN: %S/test_errors.sh %s %t %flang_fc1 +! UNPACK() intrinsic function error tests +program test_unpack + integer, dimension(2) :: vector = [343, 512] + logical, dimension(2, 2) :: mask = & + reshape([.true., .false., .true., .false.], [2, 2]) + integer, dimension(2, 2) :: field = reshape([1, 2, 3, 4, 5, 6], [2, 2]) + integer, dimension(2, 1) :: bad_field = reshape([1, 2], [2, 1]) + integer :: scalar_field + integer, dimension(2, 2) :: result + result = unpack(vector, mask, field) + !ERROR: Dimension 2 of 'mask=' argument has extent 2, but 'field=' argument has extent 1 + result = unpack(vector, mask, bad_field) + result = unpack(vector, mask, scalar_field) +end program From ad6bee87e6b78881223ebd71e52e5a336ef2a65c Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 28 Jun 2021 14:20:45 -0400 Subject: [PATCH 17/24] [libc++] NFCI: Remove unused Lit parameter sanitizer_library --- libcxx/test/configs/legacy.cfg.in | 1 - libcxxabi/test/lit.site.cfg.in | 1 - 2 files changed, 2 deletions(-) diff --git a/libcxx/test/configs/legacy.cfg.in b/libcxx/test/configs/legacy.cfg.in index 9e501a68c3531..f9737e85e0dc6 100644 --- a/libcxx/test/configs/legacy.cfg.in +++ b/libcxx/test/configs/legacy.cfg.in @@ -18,7 +18,6 @@ config.enable_shared = @LIBCXX_LINK_TESTS_WITH_SHARED_LIBCXX@ config.enable_32bit = @LIBCXX_BUILD_32_BITS@ config.cxx_abi = "@LIBCXX_CXX_ABI_LIBNAME@" config.use_sanitizer = "@LLVM_USE_SANITIZER@" -config.sanitizer_library = "@LIBCXX_SANITIZER_LIBRARY@" config.configuration_variant = "@LIBCXX_LIT_VARIANT@" config.host_triple = "@LLVM_HOST_TRIPLE@" if "@TARGET_TRIPLE@": diff --git a/libcxxabi/test/lit.site.cfg.in b/libcxxabi/test/lit.site.cfg.in index 425b9f2c47618..6c4e944de556d 100644 --- a/libcxxabi/test/lit.site.cfg.in +++ b/libcxxabi/test/lit.site.cfg.in @@ -17,7 +17,6 @@ config.llvm_unwinder = @LIBCXXABI_USE_LLVM_UNWINDER@ config.builtins_library = "@LIBCXXABI_BUILTINS_LIBRARY@" config.enable_threads = @LIBCXXABI_ENABLE_THREADS@ config.use_sanitizer = "@LLVM_USE_SANITIZER@" -config.sanitizer_library = "@LIBCXXABI_SANITIZER_LIBRARY@" config.enable_32bit = @LIBCXXABI_BUILD_32_BITS@ config.target_info = "@LIBCXXABI_TARGET_INFO@" config.executor = "@LIBCXXABI_EXECUTOR@" From 43fadefb0e77c56de7637c391cf98cf709b27095 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Fri, 25 Jun 2021 10:40:08 -0700 Subject: [PATCH 18/24] [flang] Implement user-defined derived type runtime I/O With derived type description tables now available to the runtime library, it is possible to implement the concept of "child" I/O statements in the runtime and use them to convert instances of derived type I/O data transfers into calls to user-defined subroutines when they have been specified for a type. (See Fortran 2018, subclauses 12.6.4.8 & 13.7.6). - Support formatted, list-directed, and NAMELIST transfers to internal parent units; support these, and unformatted transfers, for external parent units. - Support nested child defined derived type I/O. - Parse DT'foo'(v-list) FORMAT data edit descriptors and passes their strings &/or v-list values as arguments to the defined formatted I/O routines. - Fix problems with this feature encountered in semantics and FORMAT valiation during development and end-to-end testing. - Convert typeInfo::SpecialBinding from a struct to a class after adding a member function. Differential Revision: https://reviews.llvm.org/D104930 --- flang/include/flang/Common/format.h | 10 +- flang/lib/Semantics/check-declarations.cpp | 10 +- flang/runtime/CMakeLists.txt | 1 + flang/runtime/derived.cpp | 14 +- flang/runtime/descriptor-io.cpp | 106 +++++++++++ flang/runtime/descriptor-io.h | 168 ++++++++++------- flang/runtime/format-implementation.h | 87 +++++++-- flang/runtime/format.cpp | 46 +---- flang/runtime/format.h | 29 ++- flang/runtime/io-api.cpp | 126 ++++++++----- flang/runtime/io-error.cpp | 8 + flang/runtime/io-error.h | 5 + flang/runtime/io-stmt.cpp | 205 ++++++++++++++++++--- flang/runtime/io-stmt.h | 156 +++++++++++++--- flang/runtime/tools.cpp | 4 +- flang/runtime/type-info.cpp | 68 ++++++- flang/runtime/type-info.h | 178 ++++++++++-------- flang/runtime/unit-map.cpp | 1 + flang/runtime/unit.cpp | 46 ++++- flang/runtime/unit.h | 57 +++++- flang/test/Semantics/typeinfo01.f90 | 2 +- 21 files changed, 984 insertions(+), 343 deletions(-) create mode 100644 flang/runtime/descriptor-io.cpp diff --git a/flang/include/flang/Common/format.h b/flang/include/flang/Common/format.h index 99b8cbe41d7cf..e38ea6b0dfedf 100644 --- a/flang/include/flang/Common/format.h +++ b/flang/include/flang/Common/format.h @@ -136,11 +136,11 @@ template class FormatValidator { const CHAR *cursor_{}; // current location in format_ const CHAR *laCursor_{}; // lookahead cursor Token token_{}; // current token + TokenKind previousTokenKind_{TokenKind::None}; int64_t integerValue_{-1}; // value of UnsignedInteger token Token knrToken_{}; // k, n, or r UnsignedInteger token int64_t knrValue_{-1}; // -1 ==> not present int64_t wValue_{-1}; - bool previousTokenWasInt_{false}; char argString_[3]{}; // 1-2 character msg arg; usually edit descriptor name bool formatHasErrors_{false}; bool unterminatedFormatError_{false}; @@ -179,7 +179,7 @@ template void FormatValidator::NextToken() { // At entry, cursor_ points before the start of the next token. // At exit, cursor_ points to last CHAR of token_. - previousTokenWasInt_ = token_.kind() == TokenKind::UnsignedInteger; + previousTokenKind_ = token_.kind(); CHAR c{NextChar()}; token_.set_kind(TokenKind::None); token_.set_offset(cursor_ - format_); @@ -416,7 +416,8 @@ template void FormatValidator::NextToken() { } } SetLength(); - if (stmt_ == IoStmtKind::Read) { // 13.3.2p6 + if (stmt_ == IoStmtKind::Read && + previousTokenKind_ != TokenKind::DT) { // 13.3.2p6 ReportError("String edit descriptor in READ format expression"); } else if (token_.kind() != TokenKind::String) { ReportError("Unterminated string"); @@ -829,7 +830,8 @@ template bool FormatValidator::Check() { // Possible first token of the next format item; token not yet processed. if (commaRequired) { const char *s{"Expected ',' or ')' in format expression"}; // C1302 - if (previousTokenWasInt_ && itemsWithLeadingInts_.test(token_.kind())) { + if (previousTokenKind_ == TokenKind::UnsignedInteger && + itemsWithLeadingInts_.test(token_.kind())) { ReportError(s); } else { ReportWarning(s); diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index 5d063f14499a3..b57d19b8a62e5 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -1797,9 +1797,15 @@ void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec *derivedType, void CheckHelper::CheckDioDummyIsDerived( const Symbol &subp, const Symbol &arg, GenericKind::DefinedIo ioKind) { if (const DeclTypeSpec * type{arg.GetType()}) { - const DerivedTypeSpec *derivedType{type->AsDerived()}; - if (derivedType) { + if (const DerivedTypeSpec * derivedType{type->AsDerived()}) { CheckAlreadySeenDefinedIo(derivedType, ioKind, subp); + bool isPolymorphic{type->IsPolymorphic()}; + if (isPolymorphic != IsExtensibleType(derivedType)) { + messages_.Say(arg.name(), + "Dummy argument '%s' of a defined input/output procedure must be %s when the derived type is %s"_err_en_US, + arg.name(), isPolymorphic ? "TYPE()" : "CLASS()", + isPolymorphic ? "not extensible" : "extensible"); + } } else { messages_.Say(arg.name(), "Dummy argument '%s' of a defined input/output procedure must have a" diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt index 5f4bbc73c23d2..1f7e3d14728a4 100644 --- a/flang/runtime/CMakeLists.txt +++ b/flang/runtime/CMakeLists.txt @@ -40,6 +40,7 @@ add_flang_library(FortranRuntime connection.cpp derived.cpp descriptor.cpp + descriptor-io.cpp dot-product.cpp edit-input.cpp edit-output.cpp diff --git a/flang/runtime/derived.cpp b/flang/runtime/derived.cpp index ef4bddc8a4669..4875ef2a4bc57 100644 --- a/flang/runtime/derived.cpp +++ b/flang/runtime/derived.cpp @@ -20,9 +20,9 @@ static const typeInfo::SpecialBinding *FindFinal( for (std::size_t j{0}; j < totalSpecialBindings; ++j) { const auto &special{ *specialDesc.ZeroBasedIndexedElement(j)}; - switch (special.which) { + switch (special.which()) { case typeInfo::SpecialBinding::Which::Final: - if (special.rank == rank) { + if (special.rank() == rank) { return &special; } break; @@ -40,20 +40,20 @@ static const typeInfo::SpecialBinding *FindFinal( static void CallFinalSubroutine( const Descriptor &descriptor, const typeInfo::DerivedType &derived) { if (const auto *special{FindFinal(derived, descriptor.rank())}) { - if (special->which == typeInfo::SpecialBinding::Which::ElementalFinal) { + if (special->which() == typeInfo::SpecialBinding::Which::ElementalFinal) { std::size_t byteStride{descriptor.ElementBytes()}; - auto p{reinterpret_cast(special->proc)}; + auto *p{special->GetProc()}; // Finalizable objects must be contiguous. std::size_t elements{descriptor.Elements()}; for (std::size_t j{0}; j < elements; ++j) { p(descriptor.OffsetElement(j * byteStride)); } - } else if (special->isArgDescriptorSet & 1) { - auto p{reinterpret_cast(special->proc)}; + } else if (special->IsArgDescriptor(0)) { + auto *p{special->GetProc()}; p(descriptor); } else { // Finalizable objects must be contiguous. - auto p{reinterpret_cast(special->proc)}; + auto *p{special->GetProc()}; p(descriptor.OffsetElement()); } } diff --git a/flang/runtime/descriptor-io.cpp b/flang/runtime/descriptor-io.cpp new file mode 100644 index 0000000000000..2e552b7c5228e --- /dev/null +++ b/flang/runtime/descriptor-io.cpp @@ -0,0 +1,106 @@ +//===-- runtime/descriptor-io.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "descriptor-io.h" + +namespace Fortran::runtime::io::descr { + +// User-defined derived type formatted I/O (maybe) +std::optional DefinedFormattedIo(IoStatementState &io, + const Descriptor &descriptor, const typeInfo::SpecialBinding &special) { + std::optional peek{io.GetNextDataEdit(0 /*to peek at it*/)}; + if (peek && + (peek->descriptor == DataEdit::DefinedDerivedType || + peek->descriptor == DataEdit::ListDirected)) { + // User-defined derived type formatting + IoErrorHandler &handler{io.GetIoErrorHandler()}; + DataEdit edit{*io.GetNextDataEdit()}; // consume it this time + RUNTIME_CHECK(handler, edit.descriptor == peek->descriptor); + char ioType[2 + edit.maxIoTypeChars]; + auto ioTypeLen{std::size_t{2} /*"DT"*/ + edit.ioTypeChars}; + if (edit.descriptor == DataEdit::DefinedDerivedType) { + ioType[0] = 'D'; + ioType[1] = 'T'; + std::memcpy(ioType + 2, edit.ioType, edit.ioTypeChars); + } else { + std::strcpy( + ioType, io.mutableModes().inNamelist ? "NAMELIST" : "LISTDIRECTED"); + ioTypeLen = std::strlen(ioType); + } + StaticDescriptor<0, true> statDesc; + Descriptor &vListDesc{statDesc.descriptor()}; + vListDesc.Establish(TypeCategory::Integer, sizeof(int), nullptr, 1); + vListDesc.set_base_addr(edit.vList); + vListDesc.GetDimension(0).SetBounds(1, edit.vListEntries); + vListDesc.GetDimension(0).SetByteStride( + static_cast(sizeof(int))); + ExternalFileUnit *actualExternal{io.GetExternalFileUnit()}; + ExternalFileUnit *external{actualExternal}; + if (!external) { + // Create a new unit to service defined I/O for an + // internal I/O parent. + external = &ExternalFileUnit::NewUnit(handler, true); + } + ChildIo &child{external->PushChildIo(io)}; + int unit{external->unitNumber()}; + int ioStat{IostatOk}; + char ioMsg[100]; + if (special.IsArgDescriptor(0)) { + auto *p{special.GetProc()}; + p(descriptor, unit, ioType, vListDesc, ioStat, ioMsg, ioTypeLen, + sizeof ioMsg); + } else { + auto *p{special.GetProc()}; + p(descriptor.raw().base_addr, unit, ioType, vListDesc, ioStat, ioMsg, + ioTypeLen, sizeof ioMsg); + } + handler.Forward(ioStat, ioMsg, sizeof ioMsg); + external->PopChildIo(child); + if (!actualExternal) { + // Close unit created for internal I/O above. + auto *closing{external->LookUpForClose(external->unitNumber())}; + RUNTIME_CHECK(handler, external == closing); + external->DestroyClosed(); + } + return handler.GetIoStat() == IostatOk; + } else { + // There's a user-defined I/O subroutine, but there's a FORMAT present and + // it does not have a DT data edit descriptor, so apply default formatting + // to the components of the derived type as usual. + return std::nullopt; + } +} + +// User-defined derived type unformatted I/O +bool DefinedUnformattedIo(IoStatementState &io, const Descriptor &descriptor, + const typeInfo::SpecialBinding &special) { + // Unformatted I/O must have an external unit (or child thereof). + IoErrorHandler &handler{io.GetIoErrorHandler()}; + ExternalFileUnit *external{io.GetExternalFileUnit()}; + RUNTIME_CHECK(handler, external != nullptr); + ChildIo &child{external->PushChildIo(io)}; + int unit{external->unitNumber()}; + int ioStat{IostatOk}; + char ioMsg[100]; + if (special.IsArgDescriptor(0)) { + auto *p{special.GetProc()}; + p(descriptor, unit, ioStat, ioMsg, sizeof ioMsg); + } else { + auto *p{special.GetProc()}; + p(descriptor.raw().base_addr, unit, ioStat, ioMsg, sizeof ioMsg); + } + handler.Forward(ioStat, ioMsg, sizeof ioMsg); + external->PopChildIo(child); + return handler.GetIoStat() == IostatOk; +} + +} // namespace Fortran::runtime::io::descr diff --git a/flang/runtime/descriptor-io.h b/flang/runtime/descriptor-io.h index 09d068612325b..2ebb449e46d11 100644 --- a/flang/runtime/descriptor-io.h +++ b/flang/runtime/descriptor-io.h @@ -10,6 +10,9 @@ #define FORTRAN_RUNTIME_DESCRIPTOR_IO_H_ // Implementation of I/O data list item transfers based on descriptors. +// (All I/O items come through here so that the code is exercised for test; +// some scalar I/O data transfer APIs could be changed to bypass their use +// of descriptors in the future for better efficiency.) #include "cpp-type.h" #include "descriptor.h" @@ -18,6 +21,7 @@ #include "io-stmt.h" #include "terminator.h" #include "type-info.h" +#include "unit.h" #include "flang/Common/uint128.h" namespace Fortran::runtime::io::descr { @@ -243,92 +247,130 @@ static bool DefaultFormattedComponentIO(IoStatementState &io, } } +std::optional DefinedFormattedIo( + IoStatementState &, const Descriptor &, const typeInfo::SpecialBinding &); + template static bool FormattedDerivedTypeIO( IoStatementState &io, const Descriptor &descriptor) { - Terminator &terminator{io.GetIoErrorHandler()}; + IoErrorHandler &handler{io.GetIoErrorHandler()}; + // Derived type information must be present for formatted I/O. const DescriptorAddendum *addendum{descriptor.Addendum()}; - RUNTIME_CHECK(terminator, addendum != nullptr); + RUNTIME_CHECK(handler, addendum != nullptr); const typeInfo::DerivedType *type{addendum->derivedType()}; - RUNTIME_CHECK(terminator, type != nullptr); - if (false) { - // TODO: user-defined derived type formatted I/O - } else { - // Default derived type formatting - const Descriptor &compArray{type->component()}; - RUNTIME_CHECK(terminator, compArray.rank() == 1); - std::size_t numComponents{compArray.Elements()}; - std::size_t numElements{descriptor.Elements()}; - SubscriptValue subscripts[maxRank]; - descriptor.GetLowerBounds(subscripts); - for (std::size_t j{0}; j < numElements; - ++j, descriptor.IncrementSubscripts(subscripts)) { - SubscriptValue at[maxRank]; - compArray.GetLowerBounds(at); - for (std::size_t k{0}; k < numComponents; - ++k, compArray.IncrementSubscripts(at)) { - const typeInfo::Component &component{ - *compArray.Element(at)}; - if (!DefaultFormattedComponentIO( - io, component, descriptor, subscripts, terminator)) { - return false; - } + RUNTIME_CHECK(handler, type != nullptr); + if (const typeInfo::SpecialBinding * + special{type->FindSpecialBinding(DIR == Direction::Input + ? typeInfo::SpecialBinding::Which::ReadFormatted + : typeInfo::SpecialBinding::Which::WriteFormatted)}) { + if (std::optional wasDefined{ + DefinedFormattedIo(io, descriptor, *special)}) { + return *wasDefined; // user-defined I/O was applied + } + } + // Default componentwise derived type formatting + const Descriptor &compArray{type->component()}; + RUNTIME_CHECK(handler, compArray.rank() == 1); + std::size_t numComponents{compArray.Elements()}; + std::size_t numElements{descriptor.Elements()}; + SubscriptValue subscripts[maxRank]; + descriptor.GetLowerBounds(subscripts); + for (std::size_t j{0}; j < numElements; + ++j, descriptor.IncrementSubscripts(subscripts)) { + SubscriptValue at[maxRank]; + compArray.GetLowerBounds(at); + for (std::size_t k{0}; k < numComponents; + ++k, compArray.IncrementSubscripts(at)) { + const typeInfo::Component &component{ + *compArray.Element(at)}; + if (!DefaultFormattedComponentIO( + io, component, descriptor, subscripts, handler)) { + return false; } } } return true; } +bool DefinedUnformattedIo( + IoStatementState &, const Descriptor &, const typeInfo::SpecialBinding &); + +// Unformatted I/O template -static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { - if (!io.get_if>()) { - io.GetIoErrorHandler().Crash( - "DescriptorIO() called for wrong I/O direction"); - return false; - } - if constexpr (DIR == Direction::Input) { - if (!io.BeginReadingRecord()) { - return false; - } - } - if (auto *unf{io.get_if>()}) { +static bool UnformattedDescriptorIO( + IoStatementState &io, const Descriptor &descriptor) { + IoErrorHandler &handler{io.GetIoErrorHandler()}; + const DescriptorAddendum *addendum{descriptor.Addendum()}; + const typeInfo::DerivedType *type{ + addendum ? addendum->derivedType() : nullptr}; + if (const typeInfo::SpecialBinding * + special{type + ? type->FindSpecialBinding(DIR == Direction::Input + ? typeInfo::SpecialBinding::Which::ReadUnformatted + : typeInfo::SpecialBinding::Which::WriteUnformatted) + : nullptr}) { + // User-defined derived type unformatted I/O + return DefinedUnformattedIo(io, descriptor, *special); + } else { + // Regular derived type unformatted I/O, not user-defined + auto *externalUnf{io.get_if>()}; + auto *childUnf{io.get_if>()}; + RUNTIME_CHECK(handler, externalUnf != nullptr || childUnf != nullptr); std::size_t elementBytes{descriptor.ElementBytes()}; + std::size_t numElements{descriptor.Elements()}; SubscriptValue subscripts[maxRank]; descriptor.GetLowerBounds(subscripts); - std::size_t numElements{descriptor.Elements()}; - if (false) { - // TODO: user-defined derived type unformatted I/O - } else if (descriptor.IsContiguous()) { // contiguous unformatted I/O - char &x{ExtractElement(io, descriptor, subscripts)}; - auto totalBytes{numElements * elementBytes}; + using CharType = + std::conditional_t; + auto Transfer{[=](CharType &x, std::size_t totalBytes, + std::size_t elementBytes) -> bool { if constexpr (DIR == Direction::Output) { - return unf->Emit(&x, totalBytes, elementBytes); + return externalUnf ? externalUnf->Emit(&x, totalBytes, elementBytes) + : childUnf->Emit(&x, totalBytes, elementBytes); } else { - return unf->Receive(&x, totalBytes, elementBytes); + return externalUnf ? externalUnf->Receive(&x, totalBytes, elementBytes) + : childUnf->Receive(&x, totalBytes, elementBytes); } + }}; + if (descriptor.IsContiguous()) { // contiguous unformatted I/O + char &x{ExtractElement(io, descriptor, subscripts)}; + return Transfer(x, numElements * elementBytes, elementBytes); } else { // non-contiguous unformatted I/O for (std::size_t j{0}; j < numElements; ++j) { char &x{ExtractElement(io, descriptor, subscripts)}; - if constexpr (DIR == Direction::Output) { - if (!unf->Emit(&x, elementBytes, elementBytes)) { - return false; - } - } else { - if (!unf->Receive(&x, elementBytes, elementBytes)) { - return false; - } + if (!Transfer(x, elementBytes, elementBytes)) { + return false; } if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) { - io.GetIoErrorHandler().Crash( - "DescriptorIO: subscripts out of bounds"); + handler.Crash("DescriptorIO: subscripts out of bounds"); } } return true; } - } else if (auto catAndKind{descriptor.type().GetCategoryAndKind()}) { + } +} + +template +static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { + if (!io.get_if>()) { + io.GetIoErrorHandler().Crash( + "DescriptorIO() called for wrong I/O direction"); + return false; + } + if constexpr (DIR == Direction::Input) { + if (!io.BeginReadingRecord()) { + return false; + } + } + if (!io.get_if()) { + return UnformattedDescriptorIO(io, descriptor); + } + IoErrorHandler &handler{io.GetIoErrorHandler()}; + if (auto catAndKind{descriptor.type().GetCategoryAndKind()}) { + TypeCategory cat{catAndKind->first}; int kind{catAndKind->second}; - switch (catAndKind->first) { + switch (cat) { case TypeCategory::Integer: switch (kind) { case 1: @@ -347,7 +389,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { return FormattedIntegerIO, DIR>( io, descriptor); default: - io.GetIoErrorHandler().Crash( + handler.Crash( "DescriptorIO: Unimplemented INTEGER kind (%d) in descriptor", kind); return false; @@ -368,7 +410,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { case 16: return FormattedRealIO<16, DIR>(io, descriptor); default: - io.GetIoErrorHandler().Crash( + handler.Crash( "DescriptorIO: Unimplemented REAL kind (%d) in descriptor", kind); return false; } @@ -388,7 +430,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { case 16: return FormattedComplexIO<16, DIR>(io, descriptor); default: - io.GetIoErrorHandler().Crash( + handler.Crash( "DescriptorIO: Unimplemented COMPLEX kind (%d) in descriptor", kind); return false; @@ -399,7 +441,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { return FormattedCharacterIO(io, descriptor); // TODO cases 2, 4 default: - io.GetIoErrorHandler().Crash( + handler.Crash( "DescriptorIO: Unimplemented CHARACTER kind (%d) in descriptor", kind); return false; @@ -419,7 +461,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { return FormattedLogicalIO, DIR>( io, descriptor); default: - io.GetIoErrorHandler().Crash( + handler.Crash( "DescriptorIO: Unimplemented LOGICAL kind (%d) in descriptor", kind); return false; @@ -428,7 +470,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { return FormattedDerivedTypeIO(io, descriptor); } } - io.GetIoErrorHandler().Crash("DescriptorIO: Bad type code (%d) in descriptor", + handler.Crash("DescriptorIO: Bad type code (%d) in descriptor", static_cast(descriptor.type().raw())); return false; } diff --git a/flang/runtime/format-implementation.h b/flang/runtime/format-implementation.h index 91d80a7336019..63ca682eb3e7a 100644 --- a/flang/runtime/format-implementation.h +++ b/flang/runtime/format-implementation.h @@ -338,10 +338,12 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { ++offset_; } } - if (ch == 'E' || - (!next && - (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'O' || ch == 'Z' || - ch == 'F' || ch == 'D' || ch == 'G' || ch == 'L'))) { + if ((!next && + (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'E' || ch == 'D' || + ch == 'O' || ch == 'Z' || ch == 'F' || ch == 'G' || + ch == 'L')) || + (ch == 'E' && (next == 'N' || next == 'S' || next == 'X')) || + (ch == 'D' && next == 'T')) { // Data edit descriptor found offset_ = start; return repeat && *repeat > 0 ? *repeat : 1; @@ -363,34 +365,86 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { } } +// Returns the next data edit descriptor template DataEdit FormatControl::GetNextDataEdit( Context &context, int maxRepeat) { - - // TODO: DT editing - - // Return the next data edit descriptor int repeat{CueUpNextDataEdit(context)}; auto start{offset_}; DataEdit edit; edit.descriptor = static_cast(Capitalize(GetNextChar(context))); if (edit.descriptor == 'E') { - edit.variation = static_cast(Capitalize(PeekNext())); - if (edit.variation >= 'A' && edit.variation <= 'Z') { + if (auto next{static_cast(Capitalize(PeekNext()))}; + next == 'N' || next == 'S' || next == 'X') { + edit.variation = next; ++offset_; } + } else if (edit.descriptor == 'D' && Capitalize(PeekNext()) == 'T') { + // DT'iotype'(v_list) user-defined derived type I/O + edit.descriptor = DataEdit::DefinedDerivedType; + ++offset_; + if (auto quote{static_cast(PeekNext())}; + quote == '\'' || quote == '"') { + // Capture the quoted 'iotype' + bool ok{false}, tooLong{false}; + for (++offset_; offset_ < formatLength_;) { + auto ch{static_cast(format_[offset_++])}; + if (ch == quote && + (offset_ == formatLength_ || + static_cast(format_[offset_]) != quote)) { + ok = true; + break; // that was terminating quote + } else if (edit.ioTypeChars >= edit.maxIoTypeChars) { + tooLong = true; + } else { + edit.ioType[edit.ioTypeChars++] = ch; + if (ch == quote) { + ++offset_; + } + } + } + if (!ok) { + context.SignalError( + IostatErrorInFormat, "Unclosed DT'iotype' in FORMAT"); + } else if (tooLong) { + context.SignalError( + IostatErrorInFormat, "Excessive DT'iotype' in FORMAT"); + } + } + if (PeekNext() == '(') { + // Capture the v_list arguments + bool ok{false}, tooLong{false}; + for (++offset_; offset_ < formatLength_;) { + int n{GetIntField(context)}; + if (edit.vListEntries >= edit.maxVListEntries) { + tooLong = true; + } else { + edit.vList[edit.vListEntries++] = n; + } + auto ch{static_cast(GetNextChar(context))}; + if (ch != ',') { + ok = ch == ')'; + break; + } + } + if (!ok) { + context.SignalError( + IostatErrorInFormat, "Unclosed DT(v_list) in FORMAT"); + } else if (tooLong) { + context.SignalError( + IostatErrorInFormat, "Excessive DT(v_list) in FORMAT"); + } + } } - if (edit.descriptor == 'A') { // width is optional for A[w] auto ch{PeekNext()}; if (ch >= '0' && ch <= '9') { edit.width = GetIntField(context); } - } else { + } else if (edit.descriptor != DataEdit::DefinedDerivedType) { edit.width = GetIntField(context); } - edit.modes = context.mutableModes(); - if (PeekNext() == '.') { + if (edit.descriptor != DataEdit::DefinedDerivedType && PeekNext() == '.') { ++offset_; edit.digits = GetIntField(context); CharType ch{PeekNext()}; @@ -399,14 +453,15 @@ DataEdit FormatControl::GetNextDataEdit( edit.expoDigits = GetIntField(context); } } + edit.modes = context.mutableModes(); // Handle repeated nonparenthesized edit descriptors - if (repeat > 1) { + if (repeat > maxRepeat) { stack_[height_].start = start; // after repeat count stack_[height_].remaining = repeat; // full count ++height_; } - edit.repeat = 1; + edit.repeat = std::min(1, maxRepeat); // 0 if maxRepeat==0 if (height_ > 1) { // Subtle: stack_[0].start doesn't necessarily point to '(' int start{stack_[height_ - 1].start}; if (format_[start] != '(') { diff --git a/flang/runtime/format.cpp b/flang/runtime/format.cpp index 65ed12447bb58..e46cada81aa6c 100644 --- a/flang/runtime/format.cpp +++ b/flang/runtime/format.cpp @@ -9,50 +9,6 @@ #include "format-implementation.h" namespace Fortran::runtime::io { - -DataEdit DefaultFormatControlCallbacks::GetNextDataEdit(int) { - Crash("DefaultFormatControlCallbacks::GetNextDataEdit() called for " - "non-formatted I/O statement"); - return {}; -} -bool DefaultFormatControlCallbacks::Emit( - const char *, std::size_t, std::size_t) { - Crash("DefaultFormatControlCallbacks::Emit(char) called for non-output I/O " - "statement"); - return {}; -} -bool DefaultFormatControlCallbacks::Emit(const char16_t *, std::size_t) { - Crash("DefaultFormatControlCallbacks::Emit(char16_t) called for non-output " - "I/O statement"); - return {}; -} -bool DefaultFormatControlCallbacks::Emit(const char32_t *, std::size_t) { - Crash("DefaultFormatControlCallbacks::Emit(char32_t) called for non-output " - "I/O statement"); - return {}; -} -std::optional DefaultFormatControlCallbacks::GetCurrentChar() { - Crash("DefaultFormatControlCallbacks::GetCurrentChar() called for non-input " - "I/O " - "statement"); - return {}; -} -bool DefaultFormatControlCallbacks::AdvanceRecord(int) { - Crash("DefaultFormatControlCallbacks::AdvanceRecord() called unexpectedly"); - return {}; -} -void DefaultFormatControlCallbacks::BackspaceRecord() { - Crash("DefaultFormatControlCallbacks::BackspaceRecord() called unexpectedly"); -} -void DefaultFormatControlCallbacks::HandleAbsolutePosition(std::int64_t) { - Crash("DefaultFormatControlCallbacks::HandleAbsolutePosition() called for " - "non-formatted I/O statement"); -} -void DefaultFormatControlCallbacks::HandleRelativePosition(std::int64_t) { - Crash("DefaultFormatControlCallbacks::HandleRelativePosition() called for " - "non-formatted I/O statement"); -} - template class FormatControl< InternalFormattedIoStatementState>; template class FormatControl< @@ -61,4 +17,6 @@ template class FormatControl< ExternalFormattedIoStatementState>; template class FormatControl< ExternalFormattedIoStatementState>; +template class FormatControl>; +template class FormatControl>; } // namespace Fortran::runtime::io diff --git a/flang/runtime/format.h b/flang/runtime/format.h index 9dcd59a54a8bc..77daa38f3262e 100644 --- a/flang/runtime/format.h +++ b/flang/runtime/format.h @@ -51,32 +51,28 @@ struct DataEdit { descriptor == ListDirectedImaginaryPart; } + static constexpr char DefinedDerivedType{'d'}; // DT user-defined derived type + char variation{'\0'}; // N, S, or X for EN, ES, EX std::optional width; // the 'w' field; optional for A std::optional digits; // the 'm' or 'd' field std::optional expoDigits; // 'Ee' field MutableModes modes; int repeat{1}; -}; -// FormatControl requires that A have these member functions; -// these default implementations just crash if called. -struct DefaultFormatControlCallbacks : public IoErrorHandler { - using IoErrorHandler::IoErrorHandler; - DataEdit GetNextDataEdit(int = 1); - bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); - bool Emit(const char16_t *, std::size_t); - bool Emit(const char32_t *, std::size_t); - std::optional GetCurrentChar(); - bool AdvanceRecord(int = 1); - void BackspaceRecord(); - void HandleAbsolutePosition(std::int64_t); - void HandleRelativePosition(std::int64_t); + // "iotype" &/or "v_list" values for a DT'iotype'(v_list) + // user-defined derived type data edit descriptor + static constexpr std::size_t maxIoTypeChars{32}; + static constexpr std::size_t maxVListEntries{4}; + std::uint8_t ioTypeChars{0}; + std::uint8_t vListEntries{0}; + char ioType[maxIoTypeChars]; + int vList[maxVListEntries]; }; // Generates a sequence of DataEdits from a FORMAT statement or // default-CHARACTER string. Driven by I/O item list processing. -// Errors are fatal. See clause 13.4 in Fortran 2018 for background. +// Errors are fatal. See subclause 13.4 in Fortran 2018 for background. template class FormatControl { public: using Context = CONTEXT; @@ -98,7 +94,8 @@ template class FormatControl { } // Extracts the next data edit descriptor, handling control edit descriptors - // along the way. + // along the way. If maxRepeat==0, this is a peek at the next data edit + // descriptor. DataEdit GetNextDataEdit(Context &, int maxRepeat = 1); // Emit any remaining character literals after the last data item (on output) diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp index 8754cd666ae7a..d1b13cb330eba 100644 --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -156,22 +156,29 @@ Cookie BeginExternalListIO(const char *what, int unitNumber, } ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( unitNumber, DIR, false /*!unformatted*/, terminator)}; - if (unit.access == Access::Direct) { - terminator.Crash("%s attempted on direct access file", what); - return nullptr; - } - if (!unit.isUnformatted.has_value()) { - unit.isUnformatted = false; - } - if (*unit.isUnformatted) { - terminator.Crash("%s attempted on unformatted file", what); - return nullptr; + if (ChildIo * child{unit.GetChildIo()}) { + return child->CheckFormattingAndDirection(terminator, what, false, DIR) + ? &child->BeginIoStatement>( + *child, sourceFile, sourceLine) + : nullptr; + } else { + if (unit.access == Access::Direct) { + terminator.Crash("%s attempted on direct access file", what); + return nullptr; + } + if (!unit.isUnformatted.has_value()) { + unit.isUnformatted = false; + } + if (*unit.isUnformatted) { + terminator.Crash("%s attempted on unformatted file", what); + return nullptr; + } + IoErrorHandler handler{terminator}; + unit.SetDirection(DIR, handler); + IoStatementState &io{unit.BeginIoStatement>( + std::forward(xs)..., unit, sourceFile, sourceLine)}; + return &io; } - IoErrorHandler handler{terminator}; - unit.SetDirection(DIR, handler); - IoStatementState &io{unit.BeginIoStatement>( - std::forward(xs)..., unit, sourceFile, sourceLine)}; - return &io; } Cookie IONAME(BeginExternalListOutput)( @@ -195,19 +202,29 @@ Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength, } ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( unitNumber, DIR, false /*!unformatted*/, terminator)}; - if (!unit.isUnformatted.has_value()) { - unit.isUnformatted = false; - } - if (*unit.isUnformatted) { - terminator.Crash("Formatted I/O attempted on unformatted file"); - return nullptr; + if (ChildIo * child{unit.GetChildIo()}) { + return child->CheckFormattingAndDirection(terminator, + DIR == Direction::Output ? "formatted output" + : "formatted input", + false, DIR) + ? &child->BeginIoStatement>( + *child, sourceFile, sourceLine) + : nullptr; + } else { + if (!unit.isUnformatted.has_value()) { + unit.isUnformatted = false; + } + if (*unit.isUnformatted) { + terminator.Crash("Formatted I/O attempted on unformatted file"); + return nullptr; + } + IoErrorHandler handler{terminator}; + unit.SetDirection(DIR, handler); + IoStatementState &io{ + unit.BeginIoStatement>( + unit, format, formatLength, sourceFile, sourceLine)}; + return &io; } - IoErrorHandler handler{terminator}; - unit.SetDirection(DIR, handler); - IoStatementState &io{ - unit.BeginIoStatement>( - unit, format, formatLength, sourceFile, sourceLine)}; - return &io; } Cookie IONAME(BeginExternalFormattedOutput)(const char *format, @@ -230,25 +247,36 @@ Cookie BeginUnformattedIO( Terminator terminator{sourceFile, sourceLine}; ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( unitNumber, DIR, true /*unformatted*/, terminator)}; - if (!unit.isUnformatted.has_value()) { - unit.isUnformatted = true; - } - if (!*unit.isUnformatted) { - terminator.Crash("Unformatted I/O attempted on formatted file"); - } - IoStatementState &io{unit.BeginIoStatement>( - unit, sourceFile, sourceLine)}; - IoErrorHandler handler{terminator}; - unit.SetDirection(DIR, handler); - if constexpr (DIR == Direction::Output) { - if (unit.access == Access::Sequential && !unit.isFixedRecordLength) { - // Create space for (sub)record header to be completed by - // UnformattedIoStatementState::EndIoStatement() - unit.recordLength.reset(); // in case of prior BACKSPACE - io.Emit("\0\0\0\0", 4); // placeholder for record length header + if (ChildIo * child{unit.GetChildIo()}) { + return child->CheckFormattingAndDirection(terminator, + DIR == Direction::Output ? "unformatted output" + : "unformatted input", + true, DIR) + ? &child->BeginIoStatement>( + *child, sourceFile, sourceLine) + : nullptr; + } else { + if (!unit.isUnformatted.has_value()) { + unit.isUnformatted = true; + } + if (!*unit.isUnformatted) { + terminator.Crash("Unformatted I/O attempted on formatted file"); + } + IoStatementState &io{ + unit.BeginIoStatement>( + unit, sourceFile, sourceLine)}; + IoErrorHandler handler{terminator}; + unit.SetDirection(DIR, handler); + if constexpr (DIR == Direction::Output) { + if (unit.access == Access::Sequential && !unit.isFixedRecordLength) { + // Create space for (sub)record header to be completed by + // ExternalUnformattedIoStatementState::EndIoStatement() + unit.recordLength.reset(); // in case of prior BACKSPACE + io.Emit("\0\0\0\0", 4); // placeholder for record length header + } } + return &io; } - return &io; } Cookie IONAME(BeginUnformattedOutput)( @@ -276,9 +304,7 @@ Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=) Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; - bool ignored{false}; - ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreate( - ExternalFileUnit::NewUnit(terminator), terminator, ignored)}; + ExternalFileUnit &unit{ExternalFileUnit::NewUnit(terminator)}; return &unit.BeginIoStatement( unit, false /*was an existing file*/, sourceFile, sourceLine); } @@ -895,7 +921,8 @@ bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) { bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x, std::size_t length, std::size_t elementBytes) { IoStatementState &io{*cookie}; - if (auto *unf{io.get_if>()}) { + if (auto *unf{io.get_if< + ExternalUnformattedIoStatementState>()}) { return unf->Emit(x, length, elementBytes); } io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O " @@ -910,7 +937,8 @@ bool IONAME(InputUnformattedBlock)( if (io.GetIoErrorHandler().InError()) { return false; } - if (auto *unf{io.get_if>()}) { + if (auto *unf{ + io.get_if>()}) { return unf->Receive(x, length, elementBytes); } io.GetIoErrorHandler().Crash("InputUnformattedBlock() called for an I/O " diff --git a/flang/runtime/io-error.cpp b/flang/runtime/io-error.cpp index bc835bad1dc13..19342c5aa427b 100644 --- a/flang/runtime/io-error.cpp +++ b/flang/runtime/io-error.cpp @@ -57,6 +57,14 @@ void IoErrorHandler::SignalError(int iostatOrErrno) { SignalError(iostatOrErrno, nullptr); } +void IoErrorHandler::Forward( + int ioStatOrErrno, const char *msg, std::size_t length) { + SignalError(ioStatOrErrno); + if (ioStat_ != IostatOk && (flags_ & hasIoMsg)) { + ioMsg_ = SaveDefaultCharacter(msg, length, *this); + } +} + void IoErrorHandler::SignalErrno() { SignalError(errno); } void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } diff --git a/flang/runtime/io-error.h b/flang/runtime/io-error.h index e51df9b5be866..dd2a269fef89a 100644 --- a/flang/runtime/io-error.h +++ b/flang/runtime/io-error.h @@ -32,6 +32,9 @@ class IoErrorHandler : public Terminator { void HasEndLabel() { flags_ |= hasEnd; } void HasEorLabel() { flags_ |= hasEor; } void HasIoMsg() { flags_ |= hasIoMsg; } + void HandleAnything() { + flags_ = hasIoStat | hasErr | hasEnd | hasEor | hasIoMsg; + } bool InError() const { return ioStat_ != IostatOk; } @@ -41,6 +44,8 @@ class IoErrorHandler : public Terminator { SignalError(IostatGenericError, msg, std::forward(xs)...); } + void Forward(int iostatOrErrno, const char *, std::size_t); + void SignalErrno(); // SignalError(errno) void SignalEnd(); // input only; EOF on internal write is an error void SignalEor(); // non-advancing input only; EOR on write is an error diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp index 099d9038a8acd..3432f847cce51 100644 --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -21,32 +21,64 @@ namespace Fortran::runtime::io { int IoStatementBase::EndIoStatement() { return GetIoStat(); } +bool IoStatementBase::Emit(const char *, std::size_t, std::size_t) { + return false; +} + +bool IoStatementBase::Emit(const char *, std::size_t) { + return false; +} + +bool IoStatementBase::Emit(const char16_t *, std::size_t) { + return false; +} + +bool IoStatementBase::Emit(const char32_t *, std::size_t) { + return false; +} + +std::optional IoStatementBase::GetCurrentChar() { + return std::nullopt; +} + +bool IoStatementBase::AdvanceRecord(int) { return false; } + +void IoStatementBase::BackspaceRecord() {} + +bool IoStatementBase::Receive(char *, std::size_t, std::size_t) { + return false; +} + std::optional IoStatementBase::GetNextDataEdit( IoStatementState &, int) { return std::nullopt; } +ExternalFileUnit *IoStatementBase::GetExternalFileUnit() const { + return nullptr; +} + +bool IoStatementBase::BeginReadingRecord() { return true; } + +void IoStatementBase::FinishReadingRecord() {} + +void IoStatementBase::HandleAbsolutePosition(std::int64_t) {} + +void IoStatementBase::HandleRelativePosition(std::int64_t) {} + bool IoStatementBase::Inquire(InquiryKeywordHash, char *, std::size_t) { - Crash( - "IoStatementBase::Inquire() called for I/O statement other than INQUIRE"); return false; } bool IoStatementBase::Inquire(InquiryKeywordHash, bool &) { - Crash( - "IoStatementBase::Inquire() called for I/O statement other than INQUIRE"); return false; } bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t, bool &) { - Crash( - "IoStatementBase::Inquire() called for I/O statement other than INQUIRE"); return false; } bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) { - Crash( - "IoStatementBase::Inquire() called for I/O statement other than INQUIRE"); return false; } @@ -69,12 +101,12 @@ InternalIoStatementState::InternalIoStatementState( template bool InternalIoStatementState::Emit( - const CharType *data, std::size_t chars, std::size_t /*elementBytes*/) { + const CharType *data, std::size_t chars) { if constexpr (DIR == Direction::Input) { Crash("InternalIoStatementState::Emit() called"); return false; } - return unit_.Emit(data, chars, *this); + return unit_.Emit(data, chars * sizeof(CharType), *this); } template @@ -252,6 +284,14 @@ bool ExternalIoStatementState::Emit( return unit().Emit(data, bytes, elementBytes, *this); } +template +bool ExternalIoStatementState::Emit(const char *data, std::size_t bytes) { + if constexpr (DIR == Direction::Input) { + Crash("ExternalIoStatementState::Emit(char) called for input statement"); + } + return unit().Emit(data, bytes, 0, *this); +} + template bool ExternalIoStatementState::Emit( const char16_t *data, std::size_t chars) { @@ -261,7 +301,7 @@ bool ExternalIoStatementState::Emit( } // TODO: UTF-8 encoding return unit().Emit(reinterpret_cast(data), chars * sizeof *data, - static_cast(sizeof *data), *this); + sizeof *data, *this); } template @@ -273,7 +313,7 @@ bool ExternalIoStatementState::Emit( } // TODO: UTF-8 encoding return unit().Emit(reinterpret_cast(data), chars * sizeof *data, - static_cast(sizeof *data), *this); + sizeof *data, *this); } template @@ -354,6 +394,24 @@ bool IoStatementState::Emit( [=](auto &x) { return x.get().Emit(data, n, elementBytes); }, u_); } +bool IoStatementState::Emit(const char *data, std::size_t n) { + return std::visit([=](auto &x) { return x.get().Emit(data, n); }, u_); +} + +bool IoStatementState::Emit(const char16_t *data, std::size_t chars) { + return std::visit([=](auto &x) { return x.get().Emit(data, chars); }, u_); +} + +bool IoStatementState::Emit(const char32_t *data, std::size_t chars) { + return std::visit([=](auto &x) { return x.get().Emit(data, chars); }, u_); +} + +bool IoStatementState::Receive( + char *data, std::size_t n, std::size_t elementBytes) { + return std::visit( + [=](auto &x) { return x.get().Receive(data, n, elementBytes); }, u_); +} + std::optional IoStatementState::GetCurrentChar() { return std::visit([&](auto &x) { return x.get().GetCurrentChar(); }, u_); } @@ -370,6 +428,10 @@ void IoStatementState::HandleRelativePosition(std::int64_t n) { std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_); } +void IoStatementState::HandleAbsolutePosition(std::int64_t n) { + std::visit([=](auto &x) { x.get().HandleAbsolutePosition(n); }, u_); +} + int IoStatementState::EndIoStatement() { return std::visit([](auto &x) { return x.get().EndIoStatement(); }, u_); } @@ -682,23 +744,100 @@ ListDirectedStatementState::GetNextDataEdit( } template -bool UnformattedIoStatementState::Receive( +bool ExternalUnformattedIoStatementState::Receive( char *data, std::size_t bytes, std::size_t elementBytes) { if constexpr (DIR == Direction::Output) { - this->Crash( - "UnformattedIoStatementState::Receive() called for output statement"); + this->Crash("ExternalUnformattedIoStatementState::Receive() called for " + "output statement"); } return this->unit().Receive(data, bytes, elementBytes, *this); } template -bool UnformattedIoStatementState::Emit( +ChildIoStatementState::ChildIoStatementState( + ChildIo &child, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, child_{child} {} + +template +MutableModes &ChildIoStatementState::mutableModes() { + return child_.parent().mutableModes(); +} + +template +ConnectionState &ChildIoStatementState::GetConnectionState() { + return child_.parent().GetConnectionState(); +} + +template +ExternalFileUnit *ChildIoStatementState::GetExternalFileUnit() const { + return child_.parent().GetExternalFileUnit(); +} + +template int ChildIoStatementState::EndIoStatement() { + auto result{IoStatementBase::EndIoStatement()}; + child_.EndIoStatement(); // annihilates *this in child_.u_ + return result; +} + +template +bool ChildIoStatementState::Emit( const char *data, std::size_t bytes, std::size_t elementBytes) { - if constexpr (DIR == Direction::Input) { - this->Crash( - "UnformattedIoStatementState::Emit() called for input statement"); - } - return ExternalIoStatementState::Emit(data, bytes, elementBytes); + return child_.parent().Emit(data, bytes, elementBytes); +} + +template +bool ChildIoStatementState::Emit(const char *data, std::size_t bytes) { + return child_.parent().Emit(data, bytes); +} + +template +bool ChildIoStatementState::Emit(const char16_t *data, std::size_t chars) { + return child_.parent().Emit(data, chars); +} + +template +bool ChildIoStatementState::Emit(const char32_t *data, std::size_t chars) { + return child_.parent().Emit(data, chars); +} + +template +std::optional ChildIoStatementState::GetCurrentChar() { + return child_.parent().GetCurrentChar(); +} + +template +void ChildIoStatementState::HandleAbsolutePosition(std::int64_t n) { + return child_.parent().HandleAbsolutePosition(n); +} + +template +void ChildIoStatementState::HandleRelativePosition(std::int64_t n) { + return child_.parent().HandleRelativePosition(n); +} + +template +ChildFormattedIoStatementState::ChildFormattedIoStatementState( + ChildIo &child, const CHAR *format, std::size_t formatLength, + const char *sourceFile, int sourceLine) + : ChildIoStatementState{child, sourceFile, sourceLine}, + mutableModes_{child.parent().mutableModes()}, format_{*this, format, + formatLength} {} + +template +int ChildFormattedIoStatementState::EndIoStatement() { + format_.Finish(*this); + return ChildIoStatementState::EndIoStatement(); +} + +template +bool ChildFormattedIoStatementState::AdvanceRecord(int) { + return false; // no can do in a child I/O +} + +template +bool ChildUnformattedIoStatementState::Receive( + char *data, std::size_t bytes, std::size_t elementBytes) { + return this->child().parent().Receive(data, bytes, elementBytes); } template class InternalIoStatementState; @@ -713,8 +852,16 @@ template class ExternalFormattedIoStatementState; template class ExternalFormattedIoStatementState; template class ExternalListIoStatementState; template class ExternalListIoStatementState; -template class UnformattedIoStatementState; -template class UnformattedIoStatementState; +template class ExternalUnformattedIoStatementState; +template class ExternalUnformattedIoStatementState; +template class ChildIoStatementState; +template class ChildIoStatementState; +template class ChildFormattedIoStatementState; +template class ChildFormattedIoStatementState; +template class ChildListIoStatementState; +template class ChildListIoStatementState; +template class ChildUnformattedIoStatementState; +template class ChildUnformattedIoStatementState; int ExternalMiscIoStatementState::EndIoStatement() { ExternalFileUnit &ext{unit()}; @@ -742,6 +889,12 @@ InquireUnitState::InquireUnitState( bool InquireUnitState::Inquire( InquiryKeywordHash inquiry, char *result, std::size_t length) { + if (unit().createdForInternalChildIo()) { + SignalError(IostatInquireInternalUnit, + "INQUIRE of unit created for defined derived type I/O of an internal " + "unit"); + return false; + } const char *str{nullptr}; switch (inquiry) { case HashInquiryKeyword("ACCESS"): @@ -1161,10 +1314,4 @@ InquireIOLengthState::InquireIOLengthState( const char *sourceFile, int sourceLine) : NoUnitIoStatementState{sourceFile, sourceLine, *this} {} -bool InquireIOLengthState::Emit( - const char *, std::size_t n, std::size_t /*elementBytes*/) { - bytes_ += n; - return true; -} - } // namespace Fortran::runtime::io diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h index b76c5202619b7..34c4a47363c0d 100644 --- a/flang/runtime/io-stmt.h +++ b/flang/runtime/io-stmt.h @@ -25,6 +25,7 @@ namespace Fortran::runtime::io { class ExternalFileUnit; +class ChildIo; class OpenStatementState; class InquireUnitState; @@ -41,7 +42,10 @@ template class InternalListIoStatementState; template class ExternalFormattedIoStatementState; template class ExternalListIoStatementState; -template class UnformattedIoStatementState; +template class ExternalUnformattedIoStatementState; +template class ChildFormattedIoStatementState; +template class ChildListIoStatementState; +template class ChildUnformattedIoStatementState; struct InputStatementState {}; struct OutputStatementState {}; @@ -60,17 +64,19 @@ class IoStatementState { // to interact with the state of the I/O statement in progress. // This design avoids virtual member functions and function pointers, // which may not have good support in some runtime environments. - std::optional GetNextDataEdit(int = 1); - bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); + int EndIoStatement(); + bool Emit(const char *, std::size_t, std::size_t elementBytes); + bool Emit(const char *, std::size_t); + bool Emit(const char16_t *, std::size_t chars); + bool Emit(const char32_t *, std::size_t chars); + bool Receive(char *, std::size_t, std::size_t elementBytes = 0); std::optional GetCurrentChar(); // vacant after end of record bool AdvanceRecord(int = 1); void BackspaceRecord(); void HandleRelativePosition(std::int64_t); - int EndIoStatement(); - ConnectionState &GetConnectionState(); - IoErrorHandler &GetIoErrorHandler() const; + void HandleAbsolutePosition(std::int64_t); // for r* in list I/O + std::optional GetNextDataEdit(int = 1); ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit - MutableModes &mutableModes(); bool BeginReadingRecord(); void FinishReadingRecord(); bool Inquire(InquiryKeywordHash, char *, std::size_t); @@ -78,6 +84,10 @@ class IoStatementState { bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING= bool Inquire(InquiryKeywordHash, std::int64_t &); + MutableModes &mutableModes(); + ConnectionState &GetConnectionState(); + IoErrorHandler &GetIoErrorHandler() const; + // N.B.: this also works with base classes template A *get_if() const { return std::visit( @@ -129,8 +139,18 @@ class IoStatementState { ExternalFormattedIoStatementState>, std::reference_wrapper>, std::reference_wrapper>, - std::reference_wrapper>, - std::reference_wrapper>, + std::reference_wrapper< + ExternalUnformattedIoStatementState>, + std::reference_wrapper< + ExternalUnformattedIoStatementState>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper>, + std::reference_wrapper< + ChildUnformattedIoStatementState>, + std::reference_wrapper< + ChildUnformattedIoStatementState>, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, @@ -140,18 +160,30 @@ class IoStatementState { }; // Base class for all per-I/O statement state classes. -// Inherits IoErrorHandler from its base. -struct IoStatementBase : public DefaultFormatControlCallbacks { - using DefaultFormatControlCallbacks::DefaultFormatControlCallbacks; +struct IoStatementBase : public IoErrorHandler { + using IoErrorHandler::IoErrorHandler; + + // These are default no-op backstops that can be overridden by descendants. int EndIoStatement(); + bool Emit(const char *, std::size_t, std::size_t elementBytes); + bool Emit(const char *, std::size_t); + bool Emit(const char16_t *, std::size_t chars); + bool Emit(const char32_t *, std::size_t chars); + bool Receive(char *, std::size_t, std::size_t elementBytes = 0); + std::optional GetCurrentChar(); + bool AdvanceRecord(int); + void BackspaceRecord(); + void HandleRelativePosition(std::int64_t); + void HandleAbsolutePosition(std::int64_t); std::optional GetNextDataEdit(IoStatementState &, int = 1); - ExternalFileUnit *GetExternalFileUnit() const { return nullptr; } - bool BeginReadingRecord() { return true; } - void FinishReadingRecord() {} + ExternalFileUnit *GetExternalFileUnit() const; + bool BeginReadingRecord(); + void FinishReadingRecord(); bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); bool Inquire(InquiryKeywordHash, std::int64_t &); + void BadInquiryKeywordHashCrash(InquiryKeywordHash); }; @@ -207,8 +239,11 @@ class InternalIoStatementState : public IoStatementBase, InternalIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); int EndIoStatement(); - bool Emit(const CharType *, std::size_t chars /* not necessarily bytes */, - std::size_t elementBytes = 0); + + using IoStatementBase::Emit; + bool Emit( + const CharType *data, std::size_t chars /* not necessarily bytes */); + std::optional GetCurrentChar(); bool AdvanceRecord(int = 1); void BackspaceRecord(); @@ -275,7 +310,7 @@ class ExternalIoStatementBase : public IoStatementBase { MutableModes &mutableModes(); ConnectionState &GetConnectionState(); int EndIoStatement(); - ExternalFileUnit *GetExternalFileUnit() { return &unit_; } + ExternalFileUnit *GetExternalFileUnit() const { return &unit_; } private: ExternalFileUnit &unit_; @@ -287,7 +322,8 @@ class ExternalIoStatementState : public ExternalIoStatementBase, public: using ExternalIoStatementBase::ExternalIoStatementBase; int EndIoStatement(); - bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); + bool Emit(const char *, std::size_t, std::size_t elementBytes); + bool Emit(const char *, std::size_t); bool Emit(const char16_t *, std::size_t chars /* not bytes */); bool Emit(const char32_t *, std::size_t chars /* not bytes */); std::optional GetCurrentChar(); @@ -331,13 +367,73 @@ class ExternalListIoStatementState : public ExternalIoStatementState, }; template -class UnformattedIoStatementState : public ExternalIoStatementState { +class ExternalUnformattedIoStatementState + : public ExternalIoStatementState { public: using ExternalIoStatementState::ExternalIoStatementState; bool Receive(char *, std::size_t, std::size_t elementBytes = 0); - bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); }; +template +class ChildIoStatementState : public IoStatementBase, + public IoDirectionState { +public: + ChildIoStatementState( + ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0); + ChildIo &child() { return child_; } + MutableModes &mutableModes(); + ConnectionState &GetConnectionState(); + ExternalFileUnit *GetExternalFileUnit() const; + int EndIoStatement(); + bool Emit(const char *, std::size_t, std::size_t elementBytes); + bool Emit(const char *, std::size_t); + bool Emit(const char16_t *, std::size_t chars /* not bytes */); + bool Emit(const char32_t *, std::size_t chars /* not bytes */); + std::optional GetCurrentChar(); + void HandleRelativePosition(std::int64_t); + void HandleAbsolutePosition(std::int64_t); + +private: + ChildIo &child_; +}; + +template +class ChildFormattedIoStatementState : public ChildIoStatementState, + public FormattedIoStatementState { +public: + using CharType = CHAR; + ChildFormattedIoStatementState(ChildIo &, const CharType *format, + std::size_t formatLength, const char *sourceFile = nullptr, + int sourceLine = 0); + MutableModes &mutableModes() { return mutableModes_; } + int EndIoStatement(); + bool AdvanceRecord(int = 1); + std::optional GetNextDataEdit( + IoStatementState &, int maxRepeat = 1) { + return format_.GetNextDataEdit(*this, maxRepeat); + } + +private: + MutableModes mutableModes_; + FormatControl format_; +}; + +template +class ChildListIoStatementState : public ChildIoStatementState, + public ListDirectedStatementState { +public: + using ChildIoStatementState::ChildIoStatementState; + using ListDirectedStatementState::GetNextDataEdit; +}; + +template +class ChildUnformattedIoStatementState : public ChildIoStatementState { +public: + using ChildIoStatementState::ChildIoStatementState; + bool Receive(char *, std::size_t, std::size_t elementBytes = 0); +}; + +// OPEN class OpenStatementState : public ExternalIoStatementBase { public: OpenStatementState(ExternalFileUnit &unit, bool wasExtant, @@ -415,8 +511,17 @@ extern template class ExternalFormattedIoStatementState; extern template class ExternalFormattedIoStatementState; extern template class ExternalListIoStatementState; extern template class ExternalListIoStatementState; -extern template class UnformattedIoStatementState; -extern template class UnformattedIoStatementState; +extern template class ExternalUnformattedIoStatementState; +extern template class ExternalUnformattedIoStatementState; +extern template class ChildIoStatementState; +extern template class ChildIoStatementState; +extern template class ChildFormattedIoStatementState; +extern template class ChildFormattedIoStatementState; +extern template class ChildListIoStatementState; +extern template class ChildListIoStatementState; +extern template class ChildUnformattedIoStatementState; +extern template class ChildUnformattedIoStatementState; + extern template class FormatControl< InternalFormattedIoStatementState>; extern template class FormatControl< @@ -425,6 +530,10 @@ extern template class FormatControl< ExternalFormattedIoStatementState>; extern template class FormatControl< ExternalFormattedIoStatementState>; +extern template class FormatControl< + ChildFormattedIoStatementState>; +extern template class FormatControl< + ChildFormattedIoStatementState>; class InquireUnitState : public ExternalIoStatementBase { public: @@ -463,7 +572,6 @@ class InquireIOLengthState : public NoUnitIoStatementState, public: InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0); std::size_t bytes() const { return bytes_; } - bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); private: std::size_t bytes_{0}; diff --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp index c67da77e0c118..07f38cdf3efa5 100644 --- a/flang/runtime/tools.cpp +++ b/flang/runtime/tools.cpp @@ -71,9 +71,11 @@ int IdentifyValue( void ToFortranDefaultCharacter( char *to, std::size_t toLength, const char *from) { std::size_t len{std::strlen(from)}; - std::memcpy(to, from, std::max(toLength, len)); if (len < toLength) { + std::memcpy(to, from, len); std::memset(to + len, ' ', toLength - len); + } else { + std::memcpy(to, from, toLength); } } diff --git a/flang/runtime/type-info.cpp b/flang/runtime/type-info.cpp index df72fc466a29b..9385eabf2dc84 100644 --- a/flang/runtime/type-info.cpp +++ b/flang/runtime/type-info.cpp @@ -82,6 +82,21 @@ const Component *DerivedType::FindDataComponent( : nullptr; } +const SpecialBinding *DerivedType::FindSpecialBinding( + SpecialBinding::Which which) const { + const Descriptor &specialDesc{special()}; + std::size_t n{specialDesc.Elements()}; + SubscriptValue at[maxRank]; + specialDesc.GetLowerBounds(at); + for (std::size_t j{0}; j < n; ++j, specialDesc.IncrementSubscripts(at)) { + const SpecialBinding &special{*specialDesc.Element(at)}; + if (special.which() == which) { + return &special; + } + } + return nullptr; +} + static void DumpScalarCharacter( FILE *f, const Descriptor &desc, const char *what) { if (desc.raw().version == CFI_VERSION && @@ -103,7 +118,7 @@ FILE *DerivedType::Dump(FILE *f) const { int offset{j * static_cast(sizeof *uints)}; std::fprintf(f, " [+%3d](0x%p) %#016jx", offset, reinterpret_cast(&uints[j]), - static_cast(uints[j])); + static_cast(uints[j])); if (offset == offsetof(DerivedType, binding_)) { std::fputs(" <-- binding_\n", f); } else if (offset == offsetof(DerivedType, name_)) { @@ -151,6 +166,15 @@ FILE *DerivedType::Dump(FILE *f) const { std::fputs(" bad descriptor: ", f); compDesc.Dump(f); } + const Descriptor &specialDesc{special()}; + std::fprintf( + f, "\n special descriptor (byteSize 0x%zx): ", special_.byteSize); + specialDesc.Dump(f); + std::size_t specials{specialDesc.Elements()}; + for (std::size_t j{0}; j < specials; ++j) { + std::fprintf(f, " [%3zd] ", j); + specialDesc.ZeroBasedIndexedElement(j)->Dump(f); + } return f; } @@ -174,4 +198,46 @@ FILE *Component::Dump(FILE *f) const { return f; } +FILE *SpecialBinding::Dump(FILE *f) const { + std::fprintf( + f, "SpecialBinding @ 0x%p:\n", reinterpret_cast(this)); + switch (which_) { + case Which::Assignment: + std::fputs(" Assignment", f); + break; + case Which::ElementalAssignment: + std::fputs(" ElementalAssignment", f); + break; + case Which::Final: + std::fputs(" Final", f); + break; + case Which::ElementalFinal: + std::fputs(" ElementalFinal", f); + break; + case Which::AssumedRankFinal: + std::fputs(" AssumedRankFinal", f); + break; + case Which::ReadFormatted: + std::fputs(" ReadFormatted", f); + break; + case Which::ReadUnformatted: + std::fputs(" ReadUnformatted", f); + break; + case Which::WriteFormatted: + std::fputs(" WriteFormatted", f); + break; + case Which::WriteUnformatted: + std::fputs(" WriteUnformatted", f); + break; + default: + std::fprintf( + f, " Unknown which: 0x%x", static_cast(which_)); + break; + } + std::fprintf(f, "\n rank: %d\n", rank_); + std::fprintf(f, " isArgDescriptoSetr: 0x%x\n", isArgDescriptorSet_); + std::fprintf(f, " proc: 0x%p\n", reinterpret_cast(proc_)); + return f; +} + } // namespace Fortran::runtime::typeInfo diff --git a/flang/runtime/type-info.h b/flang/runtime/type-info.h index 05a4c41a34997..0dfb4b64ffd35 100644 --- a/flang/runtime/type-info.h +++ b/flang/runtime/type-info.h @@ -20,81 +20,7 @@ namespace Fortran::runtime::typeInfo { -class Component; - -class DerivedType { -public: - ~DerivedType(); // never defined - - const Descriptor &binding() const { return binding_.descriptor(); } - const Descriptor &name() const { return name_.descriptor(); } - std::uint64_t sizeInBytes() const { return sizeInBytes_; } - const Descriptor &parent() const { return parent_.descriptor(); } - std::uint64_t typeHash() const { return typeHash_; } - const Descriptor &uninstatiated() const { - return uninstantiated_.descriptor(); - } - const Descriptor &kindParameter() const { - return kindParameter_.descriptor(); - } - const Descriptor &lenParameterKind() const { - return lenParameterKind_.descriptor(); - } - const Descriptor &component() const { return component_.descriptor(); } - const Descriptor &procPtr() const { return procPtr_.descriptor(); } - const Descriptor &special() const { return special_.descriptor(); } - - std::size_t LenParameters() const { return lenParameterKind().Elements(); } - - // Finds a data component by name in this derived type or tis ancestors. - const Component *FindDataComponent( - const char *name, std::size_t nameLen) const; - - FILE *Dump(FILE * = stdout) const; - -private: - // This member comes first because it's used like a vtable by generated code. - // It includes all of the ancestor types' bindings, if any, first, - // with any overrides from descendants already applied to them. Local - // bindings then follow in alphabetic order of binding name. - StaticDescriptor<1, true> - binding_; // TYPE(BINDING), DIMENSION(:), POINTER, CONTIGUOUS - - StaticDescriptor<0> name_; // CHARACTER(:), POINTER - - std::uint64_t sizeInBytes_{0}; - StaticDescriptor<0, true> parent_; // TYPE(DERIVEDTYPE), POINTER - - // Instantiations of a parameterized derived type with KIND type - // parameters will point this data member to the description of - // the original uninstantiated type, which may be shared from a - // module via use association. The original uninstantiated derived - // type description will point to itself. Derived types that have - // no KIND type parameters will have a null pointer here. - StaticDescriptor<0, true> uninstantiated_; // TYPE(DERIVEDTYPE), POINTER - - // TODO: flags for SEQUENCE, BIND(C), any PRIVATE component(? see 7.5.2) - std::uint64_t typeHash_{0}; - - // These pointer targets include all of the items from the parent, if any. - StaticDescriptor<1> kindParameter_; // pointer to rank-1 array of INTEGER(8) - StaticDescriptor<1> - lenParameterKind_; // pointer to rank-1 array of INTEGER(1) - - // This array of local data components includes the parent component. - // Components are in component order, not collation order of their names. - // It does not include procedure pointer components. - StaticDescriptor<1, true> - component_; // TYPE(COMPONENT), POINTER, DIMENSION(:), CONTIGUOUS - - // Procedure pointer components - StaticDescriptor<1, true> - procPtr_; // TYPE(PROCPTR), POINTER, DIMENSION(:), CONTIGUOUS - - // Does not include special bindings from ancestral types. - StaticDescriptor<1, true> - special_; // TYPE(SPECIALBINDING), POINTER, DIMENSION(:), CONTIGUOUS -}; +class DerivedType; using ProcedurePointer = void (*)(); // TYPE(C_FUNPTR) @@ -177,7 +103,8 @@ struct ProcPtrComponent { ProcedurePointer procInitialization; // for Genre::Procedure }; -struct SpecialBinding { +class SpecialBinding { +public: enum class Which : std::uint8_t { None = 0, Assignment = 4, @@ -189,13 +116,27 @@ struct SpecialBinding { ReadUnformatted = 17, WriteFormatted = 18, WriteUnformatted = 19 - } which{Which::None}; + }; + + Which which() const { return which_; } + int rank() const { return rank_; } + bool IsArgDescriptor(int zeroBasedArg) const { + return (isArgDescriptorSet_ >> zeroBasedArg) & 1; + } + template PROC GetProc() const { + return reinterpret_cast(proc_); + } + + FILE *Dump(FILE *) const; + +private: + Which which_{Which::None}; // Used for Which::Final only. Which::Assignment always has rank 0, as // type-bound defined assignment for rank > 0 must be elemental // due to the required passed object dummy argument, which are scalar. // User defined derived type I/O is always scalar. - std::uint8_t rank{0}; + std::uint8_t rank_{0}; // The following little bit-set identifies which dummy arguments are // passed via descriptors for their derived type arguments. @@ -222,9 +163,86 @@ struct SpecialBinding { // the case when and only when the derived type is extensible. // When false, the user derived type I/O subroutine must have been // called via a generic interface, not a generic TBP. - std::uint8_t isArgDescriptorSet{0}; + std::uint8_t isArgDescriptorSet_{0}; + + ProcedurePointer proc_{nullptr}; +}; + +class DerivedType { +public: + ~DerivedType(); // never defined + + const Descriptor &binding() const { return binding_.descriptor(); } + const Descriptor &name() const { return name_.descriptor(); } + std::uint64_t sizeInBytes() const { return sizeInBytes_; } + const Descriptor &parent() const { return parent_.descriptor(); } + std::uint64_t typeHash() const { return typeHash_; } + const Descriptor &uninstatiated() const { + return uninstantiated_.descriptor(); + } + const Descriptor &kindParameter() const { + return kindParameter_.descriptor(); + } + const Descriptor &lenParameterKind() const { + return lenParameterKind_.descriptor(); + } + const Descriptor &component() const { return component_.descriptor(); } + const Descriptor &procPtr() const { return procPtr_.descriptor(); } + const Descriptor &special() const { return special_.descriptor(); } + + std::size_t LenParameters() const { return lenParameterKind().Elements(); } + + // Finds a data component by name in this derived type or tis ancestors. + const Component *FindDataComponent( + const char *name, std::size_t nameLen) const; + + const SpecialBinding *FindSpecialBinding(SpecialBinding::Which) const; + + FILE *Dump(FILE * = stdout) const; + +private: + // This member comes first because it's used like a vtable by generated code. + // It includes all of the ancestor types' bindings, if any, first, + // with any overrides from descendants already applied to them. Local + // bindings then follow in alphabetic order of binding name. + StaticDescriptor<1, true> + binding_; // TYPE(BINDING), DIMENSION(:), POINTER, CONTIGUOUS + + StaticDescriptor<0> name_; // CHARACTER(:), POINTER + + std::uint64_t sizeInBytes_{0}; + StaticDescriptor<0, true> parent_; // TYPE(DERIVEDTYPE), POINTER + + // Instantiations of a parameterized derived type with KIND type + // parameters will point this data member to the description of + // the original uninstantiated type, which may be shared from a + // module via use association. The original uninstantiated derived + // type description will point to itself. Derived types that have + // no KIND type parameters will have a null pointer here. + StaticDescriptor<0, true> uninstantiated_; // TYPE(DERIVEDTYPE), POINTER + + // TODO: flags for SEQUENCE, BIND(C), any PRIVATE component(? see 7.5.2) + std::uint64_t typeHash_{0}; + + // These pointer targets include all of the items from the parent, if any. + StaticDescriptor<1> kindParameter_; // pointer to rank-1 array of INTEGER(8) + StaticDescriptor<1> + lenParameterKind_; // pointer to rank-1 array of INTEGER(1) + + // This array of local data components includes the parent component. + // Components are in component order, not collation order of their names. + // It does not include procedure pointer components. + StaticDescriptor<1, true> + component_; // TYPE(COMPONENT), POINTER, DIMENSION(:), CONTIGUOUS + + // Procedure pointer components + StaticDescriptor<1, true> + procPtr_; // TYPE(PROCPTR), POINTER, DIMENSION(:), CONTIGUOUS - ProcedurePointer proc{nullptr}; + // Does not include special bindings from ancestral types. + StaticDescriptor<1, true> + special_; // TYPE(SPECIALBINDING), POINTER, DIMENSION(:), CONTIGUOUS }; + } // namespace Fortran::runtime::typeInfo #endif // FORTRAN_RUNTIME_TYPE_INFO_H_ diff --git a/flang/runtime/unit-map.cpp b/flang/runtime/unit-map.cpp index 1cd2115f4aa1b..915c747371850 100644 --- a/flang/runtime/unit-map.cpp +++ b/flang/runtime/unit-map.cpp @@ -92,4 +92,5 @@ ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) { bucket_[Hash(n)].swap(chain.next); // pushes new node as list head return chain.unit; } + } // namespace Fortran::runtime::io diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp index 79f3722fb7aba..aafb71fb6d73c 100644 --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -87,8 +87,11 @@ ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) { return GetUnitMap().LookUpForClose(unit); } -int ExternalFileUnit::NewUnit(const Terminator &terminator) { - return GetUnitMap().NewUnit(terminator).unitNumber(); +ExternalFileUnit &ExternalFileUnit::NewUnit( + const Terminator &terminator, bool forChildIo) { + ExternalFileUnit &unit{GetUnitMap().NewUnit(terminator)}; + unit.createdForInternalChildIo_ = forChildIo; + return unit; } void ExternalFileUnit::OpenUnit(std::optional status, @@ -697,4 +700,43 @@ void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) { BeginRecord(); impliedEndfile_ = false; } + +ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) { + OwningPtr current{std::move(child_)}; + Terminator &terminator{parent.GetIoErrorHandler()}; + OwningPtr next{New{terminator}(parent, std::move(current))}; + child_.reset(next.release()); + return *child_; +} + +void ExternalFileUnit::PopChildIo(ChildIo &child) { + if (child_.get() != &child) { + child.parent().GetIoErrorHandler().Crash( + "ChildIo being popped is not top of stack"); + } + child_.reset(child.AcquirePrevious().release()); // deletes top child +} + +void ChildIo::EndIoStatement() { + io_.reset(); + u_.emplace(); +} + +bool ChildIo::CheckFormattingAndDirection(Terminator &terminator, + const char *what, bool unformatted, Direction direction) { + bool parentIsUnformatted{!parent_.get_if()}; + bool parentIsInput{!parent_.get_if>()}; + if (unformatted != parentIsUnformatted) { + terminator.Crash("Child %s attempted on %s parent I/O unit", what, + parentIsUnformatted ? "unformatted" : "formatted"); + return false; + } else if (parentIsInput != (direction == Direction::Input)) { + terminator.Crash("Child %s attempted on %s parent I/O unit", what, + parentIsInput ? "input" : "output"); + return false; + } else { + return true; + } +} + } // namespace Fortran::runtime::io diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h index 9634f1a95804e..68876ff536399 100644 --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -28,6 +28,7 @@ namespace Fortran::runtime::io { class UnitMap; +class ChildIo; class ExternalFileUnit : public ConnectionState, public OpenFile, @@ -36,6 +37,7 @@ class ExternalFileUnit : public ConnectionState, explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {} int unitNumber() const { return unitNumber_; } bool swapEndianness() const { return swapEndianness_; } + bool createdForInternalChildIo() const { return createdForInternalChildIo_; } static ExternalFileUnit *LookUp(int unit); static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &); @@ -46,7 +48,7 @@ class ExternalFileUnit : public ConnectionState, static ExternalFileUnit *LookUp(const char *path); static ExternalFileUnit &CreateNew(int unit, const Terminator &); static ExternalFileUnit *LookUpForClose(int unit); - static int NewUnit(const Terminator &); + static ExternalFileUnit &NewUnit(const Terminator &, bool forChildIo = false); static void CloseAll(IoErrorHandler &); static void FlushAll(IoErrorHandler &); @@ -62,7 +64,6 @@ class ExternalFileUnit : public ConnectionState, template IoStatementState &BeginIoStatement(X &&...xs) { - // TODO: Child data transfer statements vs. locking lock_.Take(); // dropped in EndIoStatement() A &state{u_.emplace(std::forward(xs)...)}; if constexpr (!std::is_same_v) { @@ -91,6 +92,10 @@ class ExternalFileUnit : public ConnectionState, BeginRecord(); } + ChildIo *GetChildIo() { return child_.get(); } + ChildIo &PushChildIo(IoStatementState &); + void PopChildIo(ChildIo &); + private: static UnitMap &GetUnitMap(); const char *FrameNextInput(IoErrorHandler &, std::size_t); @@ -116,8 +121,8 @@ class ExternalFileUnit : public ConnectionState, ExternalFormattedIoStatementState, ExternalListIoStatementState, ExternalListIoStatementState, - UnformattedIoStatementState, - UnformattedIoStatementState, InquireUnitState, + ExternalUnformattedIoStatementState, + ExternalUnformattedIoStatementState, InquireUnitState, ExternalMiscIoStatementState> u_; @@ -132,6 +137,50 @@ class ExternalFileUnit : public ConnectionState, std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber bool swapEndianness_{false}; + + bool createdForInternalChildIo_{false}; + + // A stack of child I/O pseudo-units for user-defined derived type + // I/O that have this unit number. + OwningPtr child_; +}; + +// A pseudo-unit for child I/O statements in user-defined derived type +// I/O subroutines; it forwards operations to the parent I/O statement, +// which can also be a child I/O statement. +class ChildIo { +public: + ChildIo(IoStatementState &parent, OwningPtr &&previous) + : parent_{parent}, previous_{std::move(previous)} {} + + IoStatementState &parent() const { return parent_; } + + void EndIoStatement(); + + template + IoStatementState &BeginIoStatement(X &&...xs) { + A &state{u_.emplace(std::forward(xs)...)}; + io_.emplace(state); + return *io_; + } + + OwningPtr AcquirePrevious() { return std::move(previous_); } + + bool CheckFormattingAndDirection( + Terminator &, const char *what, bool unformatted, Direction); + +private: + IoStatementState &parent_; + OwningPtr previous_; + std::variant, + ChildFormattedIoStatementState, + ChildListIoStatementState, + ChildListIoStatementState, + ChildUnformattedIoStatementState, + ChildUnformattedIoStatementState> + u_; + std::optional io_; }; } // namespace Fortran::runtime::io diff --git a/flang/test/Semantics/typeinfo01.f90 b/flang/test/Semantics/typeinfo01.f90 index a68c392ad7513..088c6e56b6b76 100644 --- a/flang/test/Semantics/typeinfo01.f90 +++ b/flang/test/Semantics/typeinfo01.f90 @@ -171,7 +171,7 @@ subroutine wu(x,u,iostat,iomsg) end module module m10 - type :: t + type, bind(c) :: t ! non-extensible end type interface read(formatted) procedure :: rf From 557e1fa02f470bd4f14b7aa4060430007332895a Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Mon, 28 Jun 2021 14:43:34 -0400 Subject: [PATCH 19/24] [lld-macho] Extend ICF to literal sections Literal sections can be deduplicated before running ICF. That makes it easy to compare them during ICF: we can tell if two literals are constant-equal by comparing their offsets in their OutputSection. LLD-ELF takes a similar approach. Reviewed By: #lld-macho, gkm Differential Revision: https://reviews.llvm.org/D104671 --- lld/MachO/Driver.cpp | 6 +-- lld/MachO/ICF.cpp | 44 +++++++++-------- lld/MachO/InputSection.cpp | 4 +- lld/MachO/Options.td | 2 +- lld/MachO/SyntheticSections.cpp | 2 +- lld/MachO/SyntheticSections.h | 2 +- lld/MachO/Writer.cpp | 12 ++++- lld/test/MachO/icf-literals.s | 86 +++++++++++++++++++++++++++++++++ 8 files changed, 127 insertions(+), 31 deletions(-) create mode 100644 lld/test/MachO/icf-literals.s diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index f91fca0999042..1d06f19311c1b 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1080,7 +1080,9 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, config->emitBitcodeBundle = args.hasArg(OPT_bitcode_bundle); config->emitDataInCodeInfo = args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, true); - config->dedupLiterals = args.hasArg(OPT_deduplicate_literals); + config->icfLevel = getICFLevel(args); + config->dedupLiterals = args.hasArg(OPT_deduplicate_literals) || + config->icfLevel != ICFLevel::none; // FIXME: Add a commandline flag for this too. config->zeroModTime = getenv("ZERO_AR_DATE"); @@ -1123,8 +1125,6 @@ bool macho::link(ArrayRef argsArr, bool canExitEarly, config->undefinedSymbolTreatment = getUndefinedSymbolTreatment(args); - config->icfLevel = getICFLevel(args); - if (config->outputType == MH_EXECUTE) config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"), /*file=*/nullptr, diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp index ce49dc903d4bc..4ff8c578d56c2 100644 --- a/lld/MachO/ICF.cpp +++ b/lld/MachO/ICF.cpp @@ -104,23 +104,22 @@ static bool equalsVariable(const ConcatInputSection *ia, if (isa(sa)) { const auto *da = dyn_cast(sa); const auto *db = dyn_cast(sb); - if (da->value != db->value) - return false; - if (da->isAbsolute() != db->isAbsolute()) - return false; - if (da->isec) { + if (da->isec && db->isec) { if (da->isec->kind() != db->isec->kind()) return false; if (const auto *isecA = dyn_cast(da->isec)) { const auto *isecB = cast(db->isec); - if (isecA->icfEqClass[icfPass % 2] != - isecB->icfEqClass[icfPass % 2]) - return false; - } else { - // FIXME: implement ICF for other InputSection kinds - return false; + return da->value == db->value && isecA->icfEqClass[icfPass % 2] == + isecB->icfEqClass[icfPass % 2]; } + // Else we have two literal sections. References to them are + // constant-equal if their offsets in the output section are equal. + return da->isec->parent == db->isec->parent && + da->isec->getOffset(da->value) == + db->isec->getOffset(db->value); } + assert(da->isAbsolute() && db->isAbsolute()); + return da->value == db->value; } else if (isa(sa)) { // There is one DylibSymbol per gotIndex and we already checked for // symbol equality, thus we know that these must be different. @@ -135,14 +134,13 @@ static bool equalsVariable(const ConcatInputSection *ia, return false; if (const auto *isecA = dyn_cast(sa)) { const auto *isecB = cast(sb); - if (isecA->icfEqClass[icfPass % 2] != isecB->icfEqClass[icfPass % 2]) - return false; + return isecA->icfEqClass[icfPass % 2] == isecB->icfEqClass[icfPass % 2]; } else { - // FIXME: implement ICF for other InputSection kinds - return false; + assert(isa(sa) || + isa(sa)); + return sa->getOffset(ra.addend) == sb->getOffset(rb.addend); } } - return true; }; return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), f); @@ -207,11 +205,15 @@ void ICF::run() { if (auto *dylibSym = dyn_cast(sym)) hash += dylibSym->stubsHelperIndex; else if (auto *defined = dyn_cast(sym)) { - hash += defined->value; - if (defined->isec) - if (auto *isec = cast(defined->isec)) - hash += isec->icfEqClass[icfPass % 2]; - // FIXME: implement ICF for other InputSection kinds + if (defined->isec) { + if (auto isec = dyn_cast(defined->isec)) + hash += defined->value + isec->icfEqClass[icfPass % 2]; + else + hash += defined->isec->kind() + + defined->isec->getOffset(defined->value); + } else { + hash += defined->value; + } } else llvm_unreachable("foldIdenticalSections symbol kind"); } diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp index 78a7f00a18c5c..a961807abd230 100644 --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -62,9 +62,7 @@ bool ConcatInputSection::isHashableForICF(bool isText) const { case S_8BYTE_LITERALS: case S_16BYTE_LITERALS: case S_LITERAL_POINTERS: - // FIXME(jezng): We should not have any ConcatInputSections of these types - // when running ICF. - return false; + llvm_unreachable("found unexpected literal type in ConcatInputSection"); case S_ZEROFILL: case S_GB_ZEROFILL: case S_NON_LAZY_SYMBOL_POINTERS: diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index b115f43594f31..ebff0d5813a02 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -55,7 +55,7 @@ def time_trace_file_eq: Joined<["--"], "time-trace-file=">, HelpText<"Specify time trace output file">, Group; def deduplicate_literals: Flag<["--"], "deduplicate-literals">, - HelpText<"Enable literal deduplication">, + HelpText<"Enable literal deduplication. This is implied by --icf={safe,all}">, Group; def print_dylib_search: Flag<["--"], "print-dylib-search">, HelpText<"Print which paths lld searched when trying to find dylibs">, diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp index 563b6e2ab605a..81fec04275295 100644 --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -1188,7 +1188,7 @@ void CStringSection::addInput(CStringInputSection *isec) { inputs.push_back(isec); } -void CStringSection::finalize() { +void CStringSection::finalizeContents() { // Add all string pieces to the string table builder to create section // contents. for (const CStringInputSection *isec : inputs) diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h index 3b2605871009a..a5f6ea9a6e1f4 100644 --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -523,7 +523,7 @@ class CStringSection final : public SyntheticSection { CStringSection(); void addInput(CStringInputSection *); uint64_t getSize() const override { return builder.getSize(); } - void finalize() override; + void finalizeContents(); bool isNeeded() const override { return !inputs.empty(); } void writeTo(uint8_t *buf) const override { builder.write(buf); } diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 5dab4d1aa3145..9dca3416875b6 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -52,6 +52,7 @@ class Writer { void scanSymbols(); template void createOutputSections(); template void createLoadCommands(); + void foldIdenticalLiterals(); void foldIdenticalSections(); void finalizeAddresses(); void finalizeLinkEditSegment(); @@ -942,6 +943,12 @@ template void Writer::createOutputSections() { linkEditSegment = getOrCreateOutputSegment(segment_names::linkEdit); } +void Writer::foldIdenticalLiterals() { + if (in.cStringSection) + in.cStringSection->finalizeContents(); + // TODO: WordLiteralSection & CFStringSection should be finalized here too +} + void Writer::foldIdenticalSections() { if (config->icfLevel == ICFLevel::none) return; @@ -973,8 +980,8 @@ void Writer::foldIdenticalSections() { else concatIsec->icfEqClass[0] = ++icfUniqueID; } - // FIXME: hash literal sections here? } + // FIXME: hash literal sections here too? parallelForEach(hashable, [](ConcatInputSection *isec) { isec->hashForICF(); }); // Now that every input section is either hashed or marked as unique, @@ -1118,6 +1125,9 @@ template void Writer::run() { in.stubHelper->setup(); scanSymbols(); createOutputSections(); + // ICF assumes that all literals have been folded already, so we must run + // foldIdenticalLiterals before foldIdenticalSections. + foldIdenticalLiterals(); foldIdenticalSections(); // After this point, we create no new segments; HOWEVER, we might // yet create branch-range extension thunks for architectures whose diff --git a/lld/test/MachO/icf-literals.s b/lld/test/MachO/icf-literals.s new file mode 100644 index 0000000000000..dbe0490dd6848 --- /dev/null +++ b/lld/test/MachO/icf-literals.s @@ -0,0 +1,86 @@ +# REQUIRES: x86 +# RUN: rm -rf %t; mkdir %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o +# RUN: %lld -lSystem --icf=all -o %t/test %t/test.o +# RUN: llvm-objdump --macho --syms -d %t/test | FileCheck %s + +# CHECK: _main: +# CHECK-NEXT: callq _foo2_ref +# CHECK-NEXT: callq _foo2_ref +# CHECK-NEXT: callq _bar2_ref +# CHECK-NEXT: callq _bar2_ref +# CHECK-NEXT: callq _baz2_ref +# CHECK-NEXT: callq _baz2_ref +# CHECK-NEXT: callq _qux2_ref +# CHECK-NEXT: callq _qux2_ref + +# CHECK: [[#%.16x,FOO:]] l O __TEXT,__cstring _foo1 +# CHECK-NEXT: [[#%.16x,FOO:]] l O __TEXT,__cstring _foo2 +# CHECK-NEXT: [[#%.16x,BAR:]] l O __TEXT,__cstring _bar1 +# CHECK-NEXT: [[#%.16x,BAR:]] l O __TEXT,__cstring _bar2 +# CHECK-NEXT: [[#%.16x,BAZ:]] l O __TEXT,__literals _baz1 +# CHECK-NEXT: [[#%.16x,BAZ:]] l O __TEXT,__literals _baz2 +# CHECK-NEXT: [[#%.16x,QUX:]] l O __TEXT,__literals _qux1 +# CHECK-NEXT: [[#%.16x,QUX:]] l O __TEXT,__literals _qux2 +# CHECK-NEXT: [[#%.16x,FOO_REF:]] l F __TEXT,__text _foo1_ref +# CHECK-NEXT: [[#%.16x,FOO_REF:]] l F __TEXT,__text _foo2_ref +# CHECK-NEXT: [[#%.16x,BAR_REF:]] l F __TEXT,__text _bar1_ref +# CHECK-NEXT: [[#%.16x,BAR_REF:]] l F __TEXT,__text _bar2_ref +# CHECK-NEXT: [[#%.16x,BAZ_REF:]] l F __TEXT,__text _baz1_ref +# CHECK-NEXT: [[#%.16x,BAZ_REF:]] l F __TEXT,__text _baz2_ref +# CHECK-NEXT: [[#%.16x,QUX_REF:]] l F __TEXT,__text _qux1_ref +# CHECK-NEXT: [[#%.16x,QUX_REF:]] l F __TEXT,__text _qux2_ref + +## _foo1 vs _bar1: same section, different offsets +## _foo1 vs _baz1: same offset, different sections + +.cstring +_foo1: + .asciz "foo" +_foo2: + .asciz "foo" +_bar1: + .asciz "bar" +_bar2: + .asciz "bar" + +.literal8 +_baz1: + .quad 0xdead +_baz2: + .quad 0xdead +_qux1: + .quad 0xbeef +_qux2: + .quad 0xbeef + +.text +_foo1_ref: + mov _foo1@GOTPCREL(%rip), %rax +_foo2_ref: + mov _foo2@GOTPCREL(%rip), %rax +_bar1_ref: + mov _bar1@GOTPCREL(%rip), %rax +_bar2_ref: + mov _bar2@GOTPCREL(%rip), %rax +_baz1_ref: + mov _baz1@GOTPCREL(%rip), %rax +_baz2_ref: + mov _baz2@GOTPCREL(%rip), %rax +_qux1_ref: + mov _qux1@GOTPCREL(%rip), %rax +_qux2_ref: + mov _qux2@GOTPCREL(%rip), %rax + +.globl _main +_main: + callq _foo1_ref + callq _foo2_ref + callq _bar1_ref + callq _bar2_ref + callq _baz1_ref + callq _baz2_ref + callq _qux1_ref + callq _qux2_ref + +.subsections_via_symbols From 74d5f30d83f44ff3835a983bcce038f334cdbd6d Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Mon, 28 Jun 2021 14:43:36 -0400 Subject: [PATCH 20/24] [lld-macho][nfc] Add absolute-vs-non-absolute symbol test for ICF Make sure we don't wrongly fold two sections that refer to symbols with the same value if they are not both absolute / non-absolute. Reviewed By: #lld-macho, gkm Differential Revision: https://reviews.llvm.org/D104876 --- lld/test/MachO/icf.s | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lld/test/MachO/icf.s b/lld/test/MachO/icf.s index 0fedb5df77110..4367646d520a8 100644 --- a/lld/test/MachO/icf.s +++ b/lld/test/MachO/icf.s @@ -13,6 +13,7 @@ # CHECK: [[#%x,A]] g F __TEXT,__text _a2 # CHECK: [[#%x,A]] g F __TEXT,__text _a3 # CHECK: [[#%x,B:]] g F __TEXT,__text _b +# CHECK: [[#%x,B2:]] g F __TEXT,__text _b2 # CHECK: [[#%x,C:]] g F __TEXT,__text _c # CHECK: [[#%x,D:]] g F __TEXT,__text _d # CHECK: [[#%x,E:]] g F __TEXT,__text _e @@ -34,6 +35,7 @@ # CHECK-NEXT: callq 0x[[#%x,A]] <_a3> # CHECK-NEXT: callq 0x[[#%x,A]] <_a3> # CHECK-NEXT: callq 0x[[#%x,B]] <_b> +# CHECK-NEXT: callq 0x[[#%x,B2]] <_b2> # CHECK-NEXT: callq 0x[[#%x,C]] <_c> # CHECK-NEXT: callq 0x[[#%x,D]] <_d> # CHECK-NEXT: callq 0x[[#%x,E]] <_e> @@ -53,14 +55,20 @@ ### TODO: ### * Fold: funcs only differ in alignment ### * No fold: func is weak? preemptable? +### * Test that we hash things appropriately w/ minimal collisions #--- abs.s .subsections_via_symbols -.globl _abs1a, _abs1b, _abs2 -_abs1a = 0xfeedfac3 -_abs1b = 0xfeedfac3 -_abs2 = 0xfeedf00d +.globl _abs1a, _abs1b, _abs2, _not_abs +_abs1a = 0xfac3 +_abs1b = 0xfac3 +_abs2 = 0xf00d + +.data +.space 0xfac3 +## _not_abs has the same Defined::value as _abs1{a,b} +_not_abs: #--- main.s .subsections_via_symbols @@ -116,6 +124,18 @@ _b: movl $0, %eax ret +### No fold: _not_abs has the same value as _abs1{a,b}, but is not absolute. + +.globl _b2 +.p2align 2 +_b2: + callq _d + mov ___nan@GOTPCREL(%rip), %rax + callq ___isnan + movabs $_not_abs, %rdx + movl $0, %eax + ret + ### No fold: _c has slightly different body from _a1 & _a2 .globl _c @@ -282,6 +302,7 @@ _main: callq _a2 callq _a3 callq _b + callq _b2 callq _c callq _d callq _e From bf457919f2db496b8fbca0a3f5f25b33c4e9b8f1 Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Sat, 26 Jun 2021 17:38:25 -0400 Subject: [PATCH 21/24] [lld-macho][nfc] Remove unnecessary dyn_cast and simplify code --- lld/MachO/UnwindInfoSection.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp index 6e9f4ff92aa27..f4bd08d2f3cbc 100644 --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -231,7 +231,7 @@ relocateCompactUnwind(ConcatOutputSection *compactUnwindSection, memcpy(buf, isec->data.data(), isec->data.size()); for (const Reloc &r : isec->relocs) { - uint64_t referentVA = 0; + uint64_t referentVA = UINT64_MAX; // Tombstone value if (auto *referentSym = r.referent.dyn_cast()) { if (!isa(referentSym)) { assert(referentSym->isInGot()); @@ -242,14 +242,12 @@ relocateCompactUnwind(ConcatOutputSection *compactUnwindSection, // that we can distinguish the null pointer case. referentVA = referentSym->gotIndex + 1; } - } else if (auto *referentIsec = r.referent.dyn_cast()) { + } else { + auto *referentIsec = r.referent.get(); ConcatInputSection *concatIsec = checkTextSegment(referentIsec); - if (concatIsec->shouldOmitFromOutput()) - referentVA = UINT64_MAX; // Tombstone value - else + if (!concatIsec->shouldOmitFromOutput()) referentVA = referentIsec->getVA(r.addend); } - writeAddress(buf + r.offset, referentVA, r.length); } } From 280593bd3ff1db6d19ccb8182698dd9c816734e2 Mon Sep 17 00:00:00 2001 From: Sameer Sahasrabuddhe Date: Mon, 28 Jun 2021 13:43:02 +0530 Subject: [PATCH 22/24] [Clang] [NFC] fix CHECK lines for convergent attribute tests --- clang/test/CodeGen/convergent-functions.cpp | 8 +++++--- clang/test/CodeGenCUDA/convergent.cu | 2 +- clang/test/CodeGenCUDA/dft-func-attr-skip-intrinsic.hip | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clang/test/CodeGen/convergent-functions.cpp b/clang/test/CodeGen/convergent-functions.cpp index 7ddb8d3f94501..cb8682474f931 100644 --- a/clang/test/CodeGen/convergent-functions.cpp +++ b/clang/test/CodeGen/convergent-functions.cpp @@ -1,8 +1,10 @@ -// RUN: %clang_cc1 -triple i386-pc-win32 -emit-llvm -fconvergent-functions -o - < %s | FileCheck -check-prefix=CONVFUNC %s -// RUN: %clang_cc1 -triple i386-pc-win32 -emit-llvm -o - < %s | FileCheck -check-prefix=NOCONVFUNC %s +// RUN: %clang_cc1 -triple i386-pc-win32 -emit-llvm -fconvergent-functions -o - < %s | FileCheck -check-prefixes=CHECK,CONVFUNC %s +// RUN: %clang_cc1 -triple i386-pc-win32 -emit-llvm -o - < %s | FileCheck -check-prefixes=CHECK,NOCONVFUNC %s // Test that the -fconvergent-functions flag works -// CONVFUNC: attributes #0 = { convergent {{.*}} } +// CHECK: attributes #0 = { // NOCONVFUNC-NOT: convergent +// CONVFUNC-SAME: convergent +// CHECK-SAME: } void func() { } diff --git a/clang/test/CodeGenCUDA/convergent.cu b/clang/test/CodeGenCUDA/convergent.cu index ff18f92ef1eae..5d98d4ba69262 100644 --- a/clang/test/CodeGenCUDA/convergent.cu +++ b/clang/test/CodeGenCUDA/convergent.cu @@ -42,4 +42,4 @@ __host__ __device__ void bar() { // HOST: declare void @_Z3bazv() [[BAZ_ATTR:#[0-9]+]] // HOST: attributes [[BAZ_ATTR]] = { // HOST-NOT: convergent -// NOST-SAME: } +// HOST-SAME: } diff --git a/clang/test/CodeGenCUDA/dft-func-attr-skip-intrinsic.hip b/clang/test/CodeGenCUDA/dft-func-attr-skip-intrinsic.hip index 9e3e436200fc3..ee4c585cb5d7c 100644 --- a/clang/test/CodeGenCUDA/dft-func-attr-skip-intrinsic.hip +++ b/clang/test/CodeGenCUDA/dft-func-attr-skip-intrinsic.hip @@ -15,4 +15,4 @@ __device__ float foo(float x) { // CHECK: attributes [[ATTR1]] = { convergent // CHECK: attributes [[ATTR2]] = { // CHECK-NOT: convergent -// CHECK: } +// CHECK-SAME: } From 614b46e4dcab0d095e05f8b4da45ef935b7b86b4 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Mon, 28 Jun 2021 18:11:22 +0200 Subject: [PATCH 23/24] [clangd] Add a flag to disable formatting of tweak edits Some tweaks might edit file types not supported by clang-format. This patch gives them a way to signal that they do not require formatting. Differential Revision: https://reviews.llvm.org/D105039 --- clang-tools-extra/clangd/ClangdServer.cpp | 4 +- clang-tools-extra/clangd/refactor/Tweak.h | 3 + .../clangd/unittests/ClangdTests.cpp | 57 +++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 0f525f3b9a0a4..1e722086e2e04 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -637,8 +637,8 @@ void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID, Effect = T.takeError(); } assert(Effect.hasValue() && "Expected at least one selection"); - if (*Effect) { - // Tweaks don't apply clang-format, do that centrally here. + if (*Effect && (*Effect)->FormatEdits) { + // Format tweaks that require it centrally here. for (auto &It : (*Effect)->ApplyEdits) { Edit &E = It.second; format::FormatStyle Style = diff --git a/clang-tools-extra/clangd/refactor/Tweak.h b/clang-tools-extra/clangd/refactor/Tweak.h index 60ee34d138d6b..5b2d9cc80d9fd 100644 --- a/clang-tools-extra/clangd/refactor/Tweak.h +++ b/clang-tools-extra/clangd/refactor/Tweak.h @@ -78,6 +78,9 @@ class Tweak { /// A message to be displayed to the user. llvm::Optional ShowMessage; FileEdits ApplyEdits; + /// Whether the edits should be formatted before presenting to the client. + /// Note that it applies to all files. + bool FormatEdits = true; static Effect showMessage(StringRef S) { Effect E; diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp index 49e1f7aa93b67..07f5da1fbc52f 100644 --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -18,12 +18,14 @@ #include "TestTU.h" #include "TidyProvider.h" #include "URI.h" +#include "refactor/Tweak.h" #include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "clang/Config/config.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Tooling/ArgumentsAdjusters.h" +#include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" @@ -31,6 +33,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" #include "llvm/Support/VirtualFileSystem.h" @@ -1259,6 +1262,60 @@ TEST(ClangdServer, MemoryUsageTest) { ASSERT_TRUE(MT.children().count("tuscheduler")); EXPECT_TRUE(MT.child("tuscheduler").children().count(FooCpp)); } + +TEST(ClangdServer, RespectsTweakFormatting) { + static constexpr const char *TweakID = "ModuleTweak"; + static constexpr const char *NewContents = "{not;\nformatted;}"; + + // Contributes a tweak that generates a non-formatted insertion and disables + // formatting. + struct TweakContributingModule final : public FeatureModule { + struct ModuleTweak final : public Tweak { + const char *id() const override { return TweakID; } + bool prepare(const Selection &Sel) override { return true; } + Expected apply(const Selection &Sel) override { + auto &SM = Sel.AST->getSourceManager(); + llvm::StringRef FilePath = SM.getFilename(Sel.Cursor); + tooling::Replacements Reps; + llvm::cantFail( + Reps.add(tooling::Replacement(FilePath, 0, 0, NewContents))); + auto E = llvm::cantFail(Effect::mainFileEdit(SM, std::move(Reps))); + E.FormatEdits = false; + return E; + } + std::string title() const override { return id(); } + llvm::StringLiteral kind() const override { + return llvm::StringLiteral(""); + }; + }; + + void contributeTweaks(std::vector> &Out) override { + Out.emplace_back(new ModuleTweak); + } + }; + + MockFS FS; + MockCompilationDatabase CDB; + auto Opts = ClangdServer::optsForTest(); + FeatureModuleSet Set; + Set.add(std::make_unique()); + Opts.FeatureModules = &Set; + ClangdServer Server(CDB, FS, Opts); + + auto FooCpp = testPath("foo.cpp"); + Server.addDocument(FooCpp, ""); + ASSERT_TRUE(Server.blockUntilIdleForTest()); + + // Ensure that disabled formatting is respected. + Notification N; + Server.applyTweak(FooCpp, {}, TweakID, [&](llvm::Expected E) { + ASSERT_TRUE(static_cast(E)); + EXPECT_THAT(llvm::cantFail(E->ApplyEdits.lookup(FooCpp).apply()), + NewContents); + N.notify(); + }); + N.wait(); +} } // namespace } // namespace clangd } // namespace clang From 2dbe1c675fe94eeb7973dcc25b049d25f4ca4fa0 Mon Sep 17 00:00:00 2001 From: Melanie Blower Date: Mon, 28 Jun 2021 15:09:27 -0400 Subject: [PATCH 24/24] [clang][PATCH][nfc] Refactor TargetInfo::adjust to pass DiagnosticsEngine to allow diagnostics on target-unsupported options Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D104729 --- clang/include/clang/Basic/TargetInfo.h | 2 +- clang/lib/Basic/TargetInfo.cpp | 2 +- clang/lib/Basic/Targets/AMDGPU.cpp | 4 ++-- clang/lib/Basic/Targets/AMDGPU.h | 2 +- clang/lib/Basic/Targets/PPC.cpp | 4 ++-- clang/lib/Basic/Targets/PPC.h | 2 +- clang/lib/Basic/Targets/SPIR.h | 4 ++-- clang/lib/Basic/Targets/WebAssembly.cpp | 3 ++- clang/lib/Basic/Targets/WebAssembly.h | 2 +- clang/lib/Frontend/ASTUnit.cpp | 2 +- clang/lib/Frontend/CompilerInstance.cpp | 4 ++-- clang/lib/Interpreter/Interpreter.cpp | 2 +- clang/tools/clang-import-test/clang-import-test.cpp | 2 +- .../Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp | 3 ++- .../Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp | 2 +- 15 files changed, 21 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index d59bad30e7428..20f6afa76cbb3 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1162,7 +1162,7 @@ class TargetInfo : public virtual TransferrableTargetInfo, /// Apply changes to the target information with respect to certain /// language options which change the target configuration and adjust /// the language based on the target options where applicable. - virtual void adjust(LangOptions &Opts); + virtual void adjust(DiagnosticsEngine &Diags, LangOptions &Opts); /// Adjust target options based on codegen options. virtual void adjustTargetOptions(const CodeGenOptions &CGOpts, diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index e73b4a3a40c74..4c2859e5eda7f 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -346,7 +346,7 @@ bool TargetInfo::isTypeSigned(IntType T) { /// Apply changes to the target information with respect to certain /// language options which change the target configuration and adjust /// the language based on the target options where applicable. -void TargetInfo::adjust(LangOptions &Opts) { +void TargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts) { if (Opts.NoBitFieldTypeAlign) UseBitFieldTypeAlignment = false; diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index 595132e2e70ba..fac786dbcf9e2 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -358,8 +358,8 @@ AMDGPUTargetInfo::AMDGPUTargetInfo(const llvm::Triple &Triple, MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; } -void AMDGPUTargetInfo::adjust(LangOptions &Opts) { - TargetInfo::adjust(Opts); +void AMDGPUTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts) { + TargetInfo::adjust(Diags, Opts); // ToDo: There are still a few places using default address space as private // address space in OpenCL, which needs to be cleaned up, then Opts.OpenCL // can be removed from the following line. diff --git a/clang/lib/Basic/Targets/AMDGPU.h b/clang/lib/Basic/Targets/AMDGPU.h index fe5c61c6ba2bb..244a6e0446905 100644 --- a/clang/lib/Basic/Targets/AMDGPU.h +++ b/clang/lib/Basic/Targets/AMDGPU.h @@ -93,7 +93,7 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo { void setAddressSpaceMap(bool DefaultIsPrivate); - void adjust(LangOptions &Opts) override; + void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override; uint64_t getPointerWidthV(unsigned AddrSpace) const override { if (isR600(getTriple())) diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp index 6860b5e5d02fa..d431dda970222 100644 --- a/clang/lib/Basic/Targets/PPC.cpp +++ b/clang/lib/Basic/Targets/PPC.cpp @@ -614,10 +614,10 @@ void PPCTargetInfo::fillValidCPUList(SmallVectorImpl &Values) const { Values.append(std::begin(ValidCPUNames), std::end(ValidCPUNames)); } -void PPCTargetInfo::adjust(LangOptions &Opts) { +void PPCTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts) { if (HasAltivec) Opts.AltiVec = 1; - TargetInfo::adjust(Opts); + TargetInfo::adjust(Diags, Opts); if (LongDoubleFormat != &llvm::APFloat::IEEEdouble()) LongDoubleFormat = Opts.PPCIEEELongDouble ? &llvm::APFloat::IEEEquad() diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h index 554f2174fee00..18ee1194c759d 100644 --- a/clang/lib/Basic/Targets/PPC.h +++ b/clang/lib/Basic/Targets/PPC.h @@ -89,7 +89,7 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo { } // Set the language option for altivec based on our value. - void adjust(LangOptions &Opts) override; + void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override; // Note: GCC recognizes the following additional cpus: // 401, 403, 405, 405fp, 440fp, 464, 464fp, 476, 476fp, 505, 740, 801, diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index c429b27709ecb..50f34abd66309 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -135,8 +135,8 @@ class LLVM_LIBRARY_VISIBILITY SPIRTargetInfo : public TargetInfo { AddrSpaceMap = DefaultIsGeneric ? &SPIRDefIsGenMap : &SPIRDefIsPrivMap; } - void adjust(LangOptions &Opts) override { - TargetInfo::adjust(Opts); + void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override { + TargetInfo::adjust(Diags, Opts); // FIXME: SYCL specification considers unannotated pointers and references // to be pointing to the generic address space. See section 5.9.3 of // SYCL 2020 specification. diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp index 2a5055c3d534b..7ef79849cb75d 100644 --- a/clang/lib/Basic/Targets/WebAssembly.cpp +++ b/clang/lib/Basic/Targets/WebAssembly.cpp @@ -234,7 +234,8 @@ ArrayRef WebAssemblyTargetInfo::getTargetBuiltins() const { Builtin::FirstTSBuiltin); } -void WebAssemblyTargetInfo::adjust(LangOptions &Opts) { +void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, + LangOptions &Opts) { // If the Atomics feature isn't available, turn off POSIXThreads and // ThreadModel, so that we don't predefine _REENTRANT or __STDCPP_THREADS__. if (!HasAtomics) { diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index 70115183e46b9..b29730c5d706b 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -138,7 +138,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { bool hasProtectedVisibility() const override { return false; } - void adjust(LangOptions &Opts) override; + void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override; }; class LLVM_LIBRARY_VISIBILITY WebAssembly32TargetInfo diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 988090a8b1b13..4f92833e4229c 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -588,7 +588,7 @@ class ASTInfoCollector : public ASTReaderListener { // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. - Target->adjust(LangOpt); + Target->adjust(PP.getDiagnostics(), LangOpt); // Initialize the preprocessor. PP.Initialize(*Target); diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 063384130f730..2ae3be6814dec 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -142,7 +142,7 @@ bool CompilerInstance::createTarget() { // Inform the target of the language options. // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. - getTarget().adjust(getLangOpts()); + getTarget().adjust(getDiagnostics(), getLangOpts()); // Adjust target options based on codegen options. getTarget().adjustTargetOptions(getCodeGenOpts(), getTargetOpts()); @@ -457,7 +457,7 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { getSourceManager(), *HeaderInfo, *this, /*IdentifierInfoLookup=*/nullptr, /*OwnsHeaderSearch=*/true, TUKind); - getTarget().adjust(getLangOpts()); + getTarget().adjust(getDiagnostics(), getLangOpts()); PP->Initialize(getTarget(), getAuxTarget()); if (PPOpts.DetailedRecord) diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 711a5e9ff0168..768847f9f0352 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -110,7 +110,7 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { "Initialization failed. " "Target is missing"); - Clang->getTarget().adjust(Clang->getLangOpts()); + Clang->getTarget().adjust(Clang->getDiagnostics(), Clang->getLangOpts()); return std::move(Clang); } diff --git a/clang/tools/clang-import-test/clang-import-test.cpp b/clang/tools/clang-import-test/clang-import-test.cpp index df173cf49f35e..fa5d7a54f53b4 100644 --- a/clang/tools/clang-import-test/clang-import-test.cpp +++ b/clang/tools/clang-import-test/clang-import-test.cpp @@ -208,7 +208,7 @@ std::unique_ptr BuildCompilerInstance() { TargetInfo *TI = TargetInfo::CreateTargetInfo( Ins->getDiagnostics(), Ins->getInvocation().TargetOpts); Ins->setTarget(TI); - Ins->getTarget().adjust(Ins->getLangOpts()); + Ins->getTarget().adjust(Ins->getDiagnostics(), Ins->getLangOpts()); Ins->createFileManager(); Ins->createSourceManager(Ins->getFileManager()); Ins->createPreprocessor(TU_Complete); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index d38e64f9c5542..af44face09ed1 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -658,7 +658,8 @@ ClangExpressionParser::ClangExpressionParser( // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. - m_compiler->getTarget().adjust(m_compiler->getLangOpts()); + m_compiler->getTarget().adjust(m_compiler->getDiagnostics(), + m_compiler->getLangOpts()); // 6. Set up the diagnostic buffer for reporting errors diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp index c337ee9f79f6b..65f8a9dcdb004 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -704,7 +704,7 @@ ClangModulesDeclVendor::Create(Target &target) { if (!instance->hasTarget()) return nullptr; - instance->getTarget().adjust(instance->getLangOpts()); + instance->getTarget().adjust(*diagnostics_engine, instance->getLangOpts()); if (!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0]))