//===-- abi_x86_64.cpp - x86_64 ABI description -----------------*- C++ -*-===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license: // // Copyright (c) 2007-2012 LDC Team. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the LDC Team nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //===----------------------------------------------------------------------===// // // The ABI implementation used for 64 bit x86 (i.e. x86_64/AMD64/x64) targets. // //===----------------------------------------------------------------------===// enum ArgClass { Integer, Sse, SseUp, X87, X87Up, ComplexX87, NoClass, Memory }; struct ABI_x86_64Layout : AbiLayout { // used to track the state of the ABI generator during // code generation uint8_t int_regs, sse_regs; ABI_x86_64Layout() : int_regs(6), sse_regs(8) { } ABI_x86_64Layout(uint8_t int_regs, uint8_t sse_regs) : int_regs(int_regs), sse_regs(sse_regs) { } struct Classification { bool isMemory; ArgClass classes[2]; Classification() : isMemory(false) { classes[0] = NoClass; classes[1] = NoClass; } void addField(unsigned offset, ArgClass cl) { if (isMemory) return; // Note that we don't need to bother checking if it crosses 8 bytes. // We don't get here with unaligned fields, and anything that can be // big enough to cross 8 bytes (cdoubles, reals, structs and arrays) // is special-cased in classifyType() int idx = (offset < 8 ? 0 : 1); ArgClass nw = merge(classes[idx], cl); if (nw != classes[idx]) { classes[idx] = nw; if (nw == Memory) { classes[1-idx] = Memory; isMemory = true; } } } static ArgClass merge(ArgClass accum, ArgClass cl) { if (accum == cl) return accum; if (accum == NoClass) return cl; if (cl == NoClass) return accum; if (accum == Memory || cl == Memory) return Memory; if (accum == Integer || cl == Integer) return Integer; if (accum == X87 || accum == X87Up || accum == ComplexX87 || cl == X87 || cl == X87Up || cl == ComplexX87) return Memory; return Sse; } }; /*else if (ty == jl_float80_type) { //if this is ever added accum.addField(offset, X87); accum.addField(offset+8, X87Up); } else if (ty->ty == jl_complex80_type) { accum.addField(offset, ComplexX87); // make sure other half knows about it too: accum.addField(offset+16, ComplexX87); } */ void classifyType(Classification& accum, jl_datatype_t *dt, uint64_t offset) const { // Floating point types if (dt == jl_float64_type || dt == jl_float32_type) { accum.addField(offset, Sse); } // Misc types else if (jl_is_cpointer_type((jl_value_t*)dt)) { accum.addField(offset, Integer); // passed as a pointer } // Ghost else if (jl_datatype_size(dt) == 0) { } // BitsTypes and not float, write as Integers else if (jl_is_primitivetype(dt)) { if (jl_datatype_size(dt) <= 8) { accum.addField(offset, Integer); } else if (jl_datatype_size(dt) <= 16) { // Int128 or other 128bit wide INTEGER types accum.addField(offset, Integer); accum.addField(offset+8, Integer); } else { accum.addField(offset, Memory); } } // struct types that map to SIMD registers else if (is_native_simd_type(dt)) { accum.addField(offset, Sse); } // Other struct types else if (jl_datatype_size(dt) <= 16 && dt->layout) { size_t i; for (i = 0; i < jl_datatype_nfields(dt); ++i) { jl_value_t *ty = jl_field_type(dt, i); if (jl_field_isptr(dt, i)) ty = (jl_value_t*)jl_voidpointer_type; else if (!jl_is_datatype(ty)) { // inline union accum.addField(offset, Memory); continue; } classifyType(accum, (jl_datatype_t*)ty, offset + jl_field_offset(dt, i)); } } else { accum.addField(offset, Memory); } } Classification classify(jl_datatype_t *dt) const { Classification cl; classifyType(cl, dt, 0); return cl; } bool use_sret(jl_datatype_t *dt, LLVMContext &ctx) override { int sret = classify(dt).isMemory; if (sret) { assert(this->int_regs > 0 && "No int regs available when determining sret-ness?"); this->int_regs--; } return sret; } bool needPassByRef(jl_datatype_t *dt, AttrBuilder &ab, LLVMContext &ctx, Type *Ty) override { Classification cl = classify(dt); if (cl.isMemory) { ab.addByValAttr(Ty); return true; } // Figure out how many registers we want for this arg: ABI_x86_64Layout wanted(0, 0); for (int i = 0 ; i < 2; i++) { if (cl.classes[i] == Integer) wanted.int_regs++; else if (cl.classes[i] == Sse) wanted.sse_regs++; } if (wanted.int_regs <= this->int_regs && wanted.sse_regs <= this->sse_regs) { this->int_regs -= wanted.int_regs; this->sse_regs -= wanted.sse_regs; } else if (jl_is_structtype(dt)) { // spill to memory even though we would ordinarily pass // it in registers ab.addByValAttr(Ty); return true; } return false; } // Called on behalf of ccall to determine preferred LLVM representation // for an argument or return value. Type *preferred_llvm_type(jl_datatype_t *dt, bool isret, LLVMContext &ctx) const override { (void) isret; // no need to rewrite these types (they are returned as pointers anyways) if (is_native_simd_type(dt)) return NULL; size_t size = jl_datatype_size(dt); size_t nbits = jl_datatype_nbits(dt); if (size > 16 || size == 0) return NULL; Classification cl = classify(dt); if (cl.isMemory) return NULL; Type *types[2]; switch (cl.classes[0]) { case Integer: if (size >= 8) types[0] = Type::getInt64Ty(ctx); else types[0] = Type::getIntNTy(ctx, nbits); break; case Sse: if (size <= 4) types[0] = Type::getFloatTy(ctx); else types[0] = Type::getDoubleTy(ctx); break; default: assert(0 && "Unexpected cl.classes[0]"); } switch (cl.classes[1]) { case NoClass: return types[0]; case Integer: assert(size > 8); types[1] = Type::getIntNTy(ctx, (nbits-64)); return StructType::get(ctx,ArrayRef(&types[0],2)); case Sse: if (size <= 12) types[1] = Type::getFloatTy(ctx); else types[1] = Type::getDoubleTy(ctx); return StructType::get(ctx,ArrayRef(&types[0],2)); default: assert(0 && "Unexpected cl.classes[0]"); } // Silence GCC assert(0); return NULL; } };