Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -296,16 +296,15 @@ fail(DL, DAG, "WebAssembly doesn't support tail call yet"); CLI.IsTailCall = false; - SmallVectorImpl &OutVals = CLI.OutVals; - SmallVectorImpl &Ins = CLI.Ins; if (Ins.size() > 1) fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); SmallVectorImpl &Outs = CLI.Outs; - for (const ISD::OutputArg &Out : Outs) { - if (Out.Flags.isByVal()) - fail(DL, DAG, "WebAssembly hasn't implemented byval arguments"); + SmallVectorImpl &OutVals = CLI.OutVals; + for (unsigned i = 0; i < Outs.size(); ++i) { + const ISD::OutputArg &Out = Outs[i]; + SDValue &OutVal = OutVals[i]; if (Out.Flags.isNest()) fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); if (Out.Flags.isInAlloca()) @@ -314,6 +313,21 @@ fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); if (Out.Flags.isInConsecutiveRegsLast()) fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); + if (Out.Flags.isByVal()) { + auto *MFI = MF.getFrameInfo(); + assert(Out.Flags.getByValSize() && "Zero-size byval?"); + int FI = MFI->CreateStackObject(Out.Flags.getByValSize(), + Out.Flags.getByValAlign(), + /*isSS=*/false); + SDValue SizeNode = + DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32); + SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + Chain = DAG.getMemcpy( + Chain, DL, FINode, OutVal, SizeNode, Out.Flags.getByValAlign(), + /*isVolatile*/ false, /*AlwaysInline=*/true, + /*isTailCall*/ false, MachinePointerInfo(), MachinePointerInfo()); + OutVal = FINode; + } } bool IsVarArg = CLI.IsVarArg; @@ -468,8 +482,6 @@ MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); for (const ISD::InputArg &In : Ins) { - if (In.Flags.isByVal()) - fail(DL, DAG, "WebAssembly hasn't implemented byval arguments"); if (In.Flags.isInAlloca()) fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); if (In.Flags.isNest()) Index: llvm/trunk/lib/Target/WebAssembly/known_gcc_test_failures.txt =================================================================== --- llvm/trunk/lib/Target/WebAssembly/known_gcc_test_failures.txt +++ llvm/trunk/lib/Target/WebAssembly/known_gcc_test_failures.txt @@ -17,7 +17,6 @@ va-arg-22.c # TargetRegisterInfo.h:315: static unsigned int llvm::TargetRegisterInfo::virtReg2Index(unsigned int): Assertion `isVirtualRegister(Reg) && "Not a virtual register"' failed. -struct-ret-1.c va-arg-11.c va-arg-21.c va-arg-24.c @@ -29,20 +28,10 @@ 930628-1.c 980707-1.c -# WebAssemblyISelLowering.cpp:316: virtual llvm::SDValue llvm::WebAssemblyTargetLowering::LowerCall(llvm::TargetLowering::CallLoweringInfo&, llvm::SmallVectorImpl&) const: Assertion `!Out.Flags.isByVal() && "byval is not valid for return values"' failed. -20030914-2.c -20040703-1.c -20081117-1.c +# Unknown type! +# UNREACHABLE executed at /s/llvm-upstream/llvm/lib/IR/ValueTypes.cpp:280! 920625-1.c -931004-11.c -931004-13.c -980223.c -bitfld-5.c -complex-7.c -pr38969.c -pr51323.c -pr52129.c -pr57130.c + # These were previously "Cannot select FrameIndex." Now most of them fail # because they contain call frame pseudos (e.g. call a vararg func), @@ -58,7 +47,6 @@ 20021024-1.c 20030828-1.c -20030914-1.c 20040302-1.c 20040625-1.c @@ -75,7 +63,6 @@ 20080506-2.c 20080519-1.c -20081103-1.c 20090113-1.c 20090113-2.c 20090113-3.c @@ -103,9 +90,6 @@ 991216-2.c -#cbrt.c -complex-5.c -complex-6.c enum-3.c fprintf-chk-1.c @@ -147,10 +131,6 @@ pr49390.c pr49419.c -#pr51877.c - -#pr52979-1.c -#pr52979-2.c pr53645-2.c pr53645.c @@ -215,49 +195,6 @@ 980526-1.c 990208-1.c -# WebAssembly hasn't implemented byval arguments. -20000412-3.c -20000419-1.c -20000706-1.c -20000706-2.c -20000707-1.c -20000717-1.c -20000717-5.c -20000808-1.c -20010605-2.c -20011113-1.c -20020215-1.c -20020810-1.c -20021118-1.c -20040707-1.c -20040709-1.c -20040709-2.c -20041201-1.c -20050713-1.c -20070614-1.c -920908-2.c -921112-1.c -921117-1.c -921123-2.c -921204-1.c -930126-1.c -930208-1.c -931004-5.c -931004-9.c -931031-1.c -950607-2.c -960416-1.c -990525-1.c -991118-1.c -bf64-1.c -complex-1.c -complex-2.c -pr15262-2.c -pr20621-1.c -pr23135.c -pr30185.c -pr42248.c - # unimplemented operation lowering. 20010122-1.c 20030323-1.c Index: llvm/trunk/test/CodeGen/WebAssembly/byval.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/byval.ll +++ llvm/trunk/test/CodeGen/WebAssembly/byval.ll @@ -0,0 +1,105 @@ +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -fast-isel | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +%SmallStruct = type { i32 } +%OddStruct = type { i32, i8, i32 } +%AlignedStruct = type { double, double } +%BigStruct = type { double, double, double, double, double, double, double, double, double, double, double, i8, i8, i8 } + +%BigArray = type { [33 x i8] } + +declare void @ext_func(%SmallStruct*) +declare void @ext_byval_func(%SmallStruct* byval) +declare void @ext_byval_func_align8(%SmallStruct* byval align 8) +declare void @ext_byval_func_alignedstruct(%AlignedStruct* byval) +declare void @ext_byval_func_bigarray(%BigArray* byval) + +; CHECK-LABEL: byval_arg +define void @byval_arg(%SmallStruct* %ptr) { + ; CHECK: .param i32 + ; Subtract 16 from SP (SP is 16-byte aligned) + ; CHECK: i32.const [[L1:.+]]=, __stack_pointer + ; CHECK-NEXT: i32.load [[L1]]=, 0([[L1]]) + ; CHECK-NEXT: i32.const [[L2:.+]]=, 16 + ; CHECK-NEXT: i32.sub [[SP:.+]]=, [[L1]], [[L2]] + ; Ensure SP is stored back before the call + ; CHECK-NEXT: i32.const [[L3:.+]]=, __stack_pointer + ; CHECK-NEXT: i32.store {{.*}}=, 0([[L3]]), [[SP]] + ; Copy the SmallStruct argument to the stack (SP+12, original SP-4) + ; CHECK-NEXT: i32.load $push[[L4:.+]]=, 0($0) + ; CHECK-NEXT: i32.store {{.*}}=, 12([[SP]]), $pop[[L4]] + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: i32.const [[L5:.+]]=, 12 + ; CHECK-NEXT: i32.add [[ARG:.+]]=, [[SP]], [[L5]] + ; CHECK-NEXT: call ext_byval_func@FUNCTION, [[L5]] + call void @ext_byval_func(%SmallStruct* byval %ptr) + ; Restore the stack + ; CHECK-NEXT: i32.const [[L6:.+]]=, 16 + ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], [[L6]] + ; CHECK-NEXT: i32.const [[L7:.+]]=, __stack_pointer + ; CHECK-NEXT: i32.store {{.*}}=, 0([[L7]]), [[SP]] + ; CHECK-NEXT: return + ret void +} + +; CHECK-LABEL: byval_arg_align8 +define void @byval_arg_align8(%SmallStruct* %ptr) { + ; CHECK: .param i32 + ; Don't check the entire SP sequence, just enough to get the alignment. + ; CHECK: i32.const [[L2:.+]]=, 16 + ; CHECK-NEXT: i32.sub [[SP:.+]]=, {{.+}}, [[L2]] + ; Copy the SmallStruct argument to the stack (SP+8, original SP-8) + ; CHECK: i32.load $push[[L4:.+]]=, 0($0):p2align=3 + ; CHECK-NEXT: i32.store {{.*}}=, 8([[SP]]):p2align=3, $pop[[L4]] + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: i32.const [[L5:.+]]=, 8 + ; CHECK-NEXT: i32.add [[ARG:.+]]=, [[SP]], [[L5]] + ; CHECK-NEXT: call ext_byval_func_align8@FUNCTION, [[L5]] + call void @ext_byval_func_align8(%SmallStruct* byval align 8 %ptr) + ret void +} + +; CHECK-LABEL: byval_arg_double +define void @byval_arg_double(%AlignedStruct* %ptr) { + ; CHECK: .param i32 + ; Subtract 16 from SP (SP is 16-byte aligned) + ; CHECK: i32.const [[L2:.+]]=, 16 + ; CHECK-NEXT: i32.sub [[SP:.+]]=, {{.+}}, [[L2]] + ; Copy the AlignedStruct argument to the stack (SP+0, original SP-16) + ; Just check the last load/store pair of the memcpy + ; CHECK: i64.load $push[[L4:.+]]=, 0($0) + ; CHECK-NEXT: i64.store {{.*}}=, 0([[SP]]), $pop[[L4]] + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: call ext_byval_func_alignedstruct@FUNCTION, [[SP]] + tail call void @ext_byval_func_alignedstruct(%AlignedStruct* byval %ptr) + ret void +} + +; CHECK-LABEL: byval_arg_big +define void @byval_arg_big(%BigArray* %ptr) { + ; CHECK: .param i32 + ; Subtract 48 from SP (SP is 16-byte aligned) + ; CHECK: i32.const [[L2:.+]]=, 48 + ; CHECK-NEXT: i32.sub [[SP:.+]]=, {{.+}}, [[L2]] + ; Copy the AlignedStruct argument to the stack (SP+12, original SP-36) + ; CHECK: i64.load $push[[L4:.+]]=, 0($0):p2align=0 + ; CHECK: i64.store {{.*}}=, 12([[SP]]):p2align=2, $pop[[L4]] + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: i32.const [[L5:.+]]=, 12 + ; CHECK-NEXT: i32.add [[ARG:.+]]=, [[SP]], [[L5]] + ; CHECK-NEXT: call ext_byval_func_bigarray@FUNCTION, [[ARG]] + call void @ext_byval_func_bigarray(%BigArray* byval %ptr) + ret void +} + +; CHECK-LABEL: byval_param +define void @byval_param(%SmallStruct* byval align 32 %ptr) { + ; CHECK: .param i32 + ; %ptr is just a pointer to a struct, so pass it directly through + ; CHECK: call ext_func@FUNCTION, $0 + call void @ext_func(%SmallStruct* %ptr) + ret void +}