diff --git a/README.md b/README.md index a29d0e147330f..7c2b5e669a5bc 100644 --- a/README.md +++ b/README.md @@ -153,8 +153,6 @@ Below is more information about TensorFlow-related build arguments. * Default value: None. * `tensorflow-swift-apis`: A path to the [tensorflow/swift-apis](https://github.com/tensorflow/swift-apis) deep learning library repository. * Default value: `tensorflow-swift-apis` if the [tensorflow/swift-apis](https://github.com/tensorflow/swift-apis) repository is cloned. Otherwise, none. -* `tensorflow-swift-bindings`: A path to the [tensorflow/swift-bindings](https://github.com/tensorflow/swift-bindings) repository. - * Default value: `tensorflow-swift-bindings` if the [tensorflow/swift-bindings](https://github.com/tensorflow/swift-bindings) repository is cloned. Otherwise, none. ### Build systems diff --git a/stdlib/public/TensorFlow/CMakeLists.txt b/stdlib/public/TensorFlow/CMakeLists.txt index 61df7492fb46c..5544b12d6da00 100644 --- a/stdlib/public/TensorFlow/CMakeLists.txt +++ b/stdlib/public/TensorFlow/CMakeLists.txt @@ -32,7 +32,6 @@ list(APPEND swift_stdlib_compile_flags "-DCOMPILING_TENSORFLOW_MODULE") set(SOURCES ArrayOps.swift - CompilerRuntime.swift CompositeMath.swift DataTypes.swift Execution.swift @@ -48,24 +47,15 @@ set(SOURCES TensorHandle.swift TensorProtocol.swift TensorShape.swift - Threading.swift - Utilities.swift) + Threading.swift) set(GYB_SOURCES ExecuteOp.swift.gyb) -# Copy TensorFlow bindings file, if it exists. -if (TENSORFLOW_SWIFT_BINDINGS) - file(GLOB_RECURSE TENSORFLOW_SWIFT_BINDINGS_SOURCES - "${TENSORFLOW_SWIFT_BINDINGS}/*.swift") - list(APPEND SOURCES "${TENSORFLOW_SWIFT_BINDINGS_SOURCES}") -endif() - # Copy TensorFlow high-level API sources, if they exist. if (TENSORFLOW_SWIFT_APIS) file(GLOB_RECURSE TENSORFLOW_SWIFT_API_SOURCES - "${TENSORFLOW_SWIFT_APIS}/Sources/*.swift" - "${TENSORFLOW_SWIFT_APIS}/Sources/Core/*.swift") + "${TENSORFLOW_SWIFT_APIS}/Sources/*.swift") message(STATUS "Using TensorFlow high-level APIs library.") list(APPEND SOURCES "${TENSORFLOW_SWIFT_API_SOURCES}") endif() diff --git a/stdlib/public/TensorFlow/CompilerRuntime.swift b/stdlib/public/TensorFlow/CompilerRuntime.swift deleted file mode 100644 index 33ebeb66be50d..0000000000000 --- a/stdlib/public/TensorFlow/CompilerRuntime.swift +++ /dev/null @@ -1,1284 +0,0 @@ -//===-- CompilerRuntime.swift ---------------------------------*- swift -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This file defines the Swift runtime support for TensorFlow computation. -// -// This file should only contain internal details: runtime-related public APIs -// should be defined in `Execution.swift`. -// -// A global context (`_ExecutionContext.global`) is used to manage all tensor -// computation and transfers. -// -// Potential TODOs: -// - Support async on platforms other than Linux and FreeBSD. -// - Revisit the concurrency model and see if Dispatch can be built without -// Foundation. -// -//===----------------------------------------------------------------------===// - -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) -import Darwin -#else -import Glibc -#endif -import CTensorFlow - -/// TraceContext contains the state needed to build a trace graph function -/// (TF_Function). As eager ops are executed in tracing mode, their -/// corresponding nodes are added to the trace graph (via -/// `addEagerOpToGraph()`). When the trace is finalized (via `finalize()`), the -/// trace graph function can then be executed (via `execute()`) by the eager -/// runtime. -private class TraceContext { - let status: CTFStatus = TF_NewStatus() - - /// The trace graph, which will be converted to a trace graph function - /// (TF_Function) upon finalizing. - let graph = TF_NewGraph() - - /// The list of inputs to the trace graph function. It starts with the inputs - /// to the function that we trace (referred to as the "tracee function" or - /// "tracee"), followed by possible additional inputs that correspond to - /// concrete tensors produced within the trace function. - /// - /// For example, if the tracee is: - /// - /// struct TensorPair : TensorGroup { - /// public var first: T - /// public var second: U - /// } - /// - /// func foo(x: TensorPair) -> Tensor { - /// let y = Tensor(1.0) - /// return x.first + x.second + y - /// } - /// - /// Then the generated trace graph function has 3 input tensors: x.first, - /// x.second, and y. - /// - /// These symbolic tensors corresond to PlaceHolder nodes in the trace graph, - /// and will be filled in when we execute the trace graph function. - // - // TODO: If some tensors in `x` are not used within `foo()`, they can be - // pruned away in the inputs to the trace graph function. - var symbolicInputs: [TF_Output] = [] - - /// The outputs obtained by executing the `tracee` for computing the trace. - var tracedOutputs: [CTensorHandle] = [] - - /// The trace context object used in TF C API calls that convert eager ops to - /// (trace) graph nodes. - let cTraceContext: CTFETraceContext - - /// The trace graph function created by `finalize()`. - var traceGraphFn: CTFFunction? - - /// The number of additional input tensors to the trace graph function, - /// created from concrete intermediate tensors in the tracee, such as `y` in - /// the code snippet above. - var additionalInputTensorCount: Int32 = -1 - - /// `dtypes` is the (flattened) list of TF_DataType of input tensors - /// to the trace function. - init(dtypes: [TF_DataType]) { - debugLog("Instiantiating TraceContext with \(dtypes.count) input tensors.") - for (i, dtype) in dtypes.enumerated() { - let desc = TF_NewOperation(graph, "Placeholder", "input_\(i)") - TF_SetAttrType(desc, "dtype", dtype) - let result = TF_FinishOperation(desc, status) - checkOk(status) - symbolicInputs.append(TF_Output(oper: result, index: 0)) - } - cTraceContext = TFE_NewTraceContext(graph) - } - - deinit { - TFE_DeleteTraceContext(cTraceContext) - TF_DeleteGraph(graph) - TF_DeleteStatus(status) - } - - func addEagerOpToGraph(_ op: CTFEOp, - _ retvals: UnsafeMutablePointer, - _ retvalCount: UnsafeMutablePointer, - _ status: CTFStatus) { - TFE_AddEagerOpToGraph(op, cTraceContext, retvals, retvalCount, status) - checkOk(status) - } - - /// Finalize the trace graph function. - func finalize(traceeBasicName: String) { - internalConsistencyCheck(traceGraphFn == nil) - var symbolicOutputs: [TF_Output] = [] - // Only add symbolic output tensors as the outputs of the trace graph function. - // For example, let the tracee be: - // func foo(x: Tensor) -> (Tensor, Tensor) { - // let y = Tensor(1.0) - // return (x + x, y) - // } - // - // Here foo() returns 2 tensors, but only the first one (as computed by x + - // x) is symbolic. The second one for y is concrete, and is computed at - // trace creation time, not trace execution time. - // Also see the comment block above finalizeAndExecuteTraceFn(). - for (i, tracedOutput) in tracedOutputs.enumerated() - where TFE_TensorHandleIsConcrete(tracedOutput) == 0 { - debugLog("Adding symbolic tracedOutput \(i) as a trace graph func output.") - symbolicOutputs.append(TFE_GetTFOutputFromTensorHandle(tracedOutput ,status)) - checkOk(status) - } - - let traceeInputCount = symbolicInputs.count - // Append concrete tensors created within the tracee as symbolic inputs to - // the generated trace graph function. - additionalInputTensorCount = TFE_FinalizeInputTensorsFromTraceContext( - cTraceContext) - for i in 0.. [CTensorHandle] { - // We must be in the `notTracing` enum mode. - internalConsistencyCheck(_RuntimeConfig.traceState.context == nil) - internalConsistencyCheck(traceGraphFn != nil) - - let tracedFunctionName = TF_FunctionName(traceGraphFn) - internalConsistencyCheck(tracedFunctionName != nil) - let eagerContext = _TFCGetGlobalEagerContext() - let op: CTFEOp! = TFE_NewOp(eagerContext, tracedFunctionName, status) - defer { TFE_DeleteOp(op) } - checkOk(status) - - let deviceName = _ExecutionContext.global.currentDeviceName - if let deviceName = deviceName { - debugLog("Placing the trace func on device \(deviceName).") - TFE_OpSetDevice(op, deviceName, status) - checkOk(status) - } - - if useXLA { - debugLog("Enabling XLA compilation") - TFE_OpSetAttrBool(op, "_XlaCompile", 1) - } - - debugLog("Adding \(traceeInputs.count) tracee input tensors.") - internalConsistencyCheck(symbolicInputs.count == traceeInputs.count - + Int(additionalInputTensorCount)) - for input in traceeInputs { - _TFCOpAddInputFromTensorHandle(op, input, status) - checkOk(status) - } - - debugLog("Adding \(additionalInputTensorCount) additional input tensors.") - for i in 0.. String { - let specializedGraph = TF_NewGraph()! - - TF_GraphCopyFunction( - specializedGraph, traceGraphFn, /*gradient*/ nil, status) - checkOk(status) - - let tracedFunctionName = TF_FunctionName(traceGraphFn) - internalConsistencyCheck(tracedFunctionName != nil) - let tracedOpDesc = TF_NewOperation( - specializedGraph, tracedFunctionName, "tracedFn") - - // Create and append the inputs to the graph function. - let traceeInputs = symbolicInputs.dropLast( - dataTensors.count + Int(additionalInputTensorCount)) - var inputs: [TF_Output] = [] - for (i, traceeInput) in traceeInputs.enumerated() { - let desc = TF_NewOperation(specializedGraph, "Placeholder", "input_\(i)") - TF_SetAttrType(desc, "dtype", TF_OperationOutputType(traceeInput)) - let result = TF_FinishOperation(desc, status) - checkOk(status) - let input = TF_Output(oper: result, index: 0) - TF_AddInput(tracedOpDesc, input) - inputs.append(input) - } - - // Wire the data to the corresponding inputs of the tracedOp. - for (i, cTensorHandle) in dataTensors.enumerated() { - let cTensor = TFE_TensorHandleResolve(cTensorHandle, status) - checkOk(status) - let desc = TF_NewOperation(specializedGraph, "Const", "input_const_\(i)") - TF_SetAttrType(desc, "dtype", TFE_TensorHandleDataType(cTensorHandle)) - TF_SetAttrTensor(desc, "value", cTensor, status) - checkOk(status) - let result = TF_FinishOperation(desc, status) - checkOk(status) - TF_AddInput(tracedOpDesc, TF_Output(oper: result, index: 0)) - } - - var nextConst = dataTensors.count - debugLog("Adding \(additionalInputTensorCount) additional input tensors.") - for i in 0..= 0 && newValue <= 4 else { - fatalError("Invalid tensorflowVerboseLogLevel value \(newValue)") - } - } - } -} - -private func configureRuntimeFromEnvironment() { - if let value = getenv("SWIFT_TENSORFLOW_ENABLE_DEBUG_LOGGING"), - String(cString: value).lowercased() == "true" { - _RuntimeConfig.printsDebugLog = true - debugLog("Turning on debug logging from env.") - } - - if let value = getenv("SWIFT_TENSORFLOW_VERBOSE_LOG_LEVEL") { - guard var verboseLevel = Int32(String(cString: value)) else { - fatalError("SWIFT_TENSORFLOW_VERBOSE_LOG_LEVEL must take an int value.") - } - if verboseLevel > 4 { - verboseLevel = 4 - } - _RuntimeConfig.tensorflowVerboseLogLevel = verboseLevel - debugLog("Setting TF logging verbose level to \(verboseLevel) from env.") - } - - if let value = getenv("SWIFT_TENSORFLOW_SERVER_ADDRESS") { - let address = String(cString: value) - debugLog("Env var SWIFT_TENSORFLOW_SERVER_ADDRESS has value \(address).") - if address == "local" { - _RuntimeConfig.session = .local - debugLog("Using local TF session.") - } else { - guard let idx = address.firstIndex(of: ":"), - let endIdx = address.index(idx, offsetBy: 3, limitedBy: address.endIndex), - address[idx.. - - /// The TFE_Context object. - @usableFromInline let eagerContext: CTFEContext - - /// The status for checking TensorFlow errors. - private let status: CTFStatus = TF_NewStatus() - - /// The mutex for preventing potential concurrent access. - private var mutex: pthread_mutex_t = pthread_mutex_t() - - /// Initializes a new execution context by initializing available devices. - @usableFromInline - init() { - configureRuntimeFromEnvironment() - - // Suppress TensorFlow logging, unless the user specified a log level. - setenv("TF_CPP_MIN_LOG_LEVEL", "3", /*override*/ 0) - - debugLog("Initializing global context.") - - // Initialize the TF runtime exactly once. Only affects local execution - // (when _RuntimeConfig.tensorFlowServer is set to ""). - if !_RuntimeConfig.tensorFlowRuntimeInitialized { - InitTensorFlowRuntime(_RuntimeConfig.printsDebugLog ? 1 : 0, - _RuntimeConfig.tensorflowVerboseLogLevel) - _RuntimeConfig.tensorFlowRuntimeInitialized = true - } - - guard let opts = TFE_NewContextOptions() else { - fatalError("ContextOptions object can never be nil.") - } - - // Create TF config object. - if _RuntimeConfig.gpuMemoryAllowGrowth { - debugLog("Allowing growth for GPU memory allocator.") - } - self.tensorFlowConfig = TF_CreateConfig( - /* enable_xla_compilation */ 0, - _RuntimeConfig.gpuMemoryAllowGrowth ? 1 : 0, - _RuntimeConfig.cpuDeviceCount) - TFE_ContextOptionsSetConfig(opts, - tensorFlowConfig.pointee.data, - tensorFlowConfig.pointee.length, - status) - checkOk(status) - - let ctx = TFE_NewContext(opts, status) - checkOk(status) - self.eagerContext = ctx! - TFE_DeleteContextOptions(opts) - checkOk(status) - - if case .remote(let serverDef) = _RuntimeConfig.session { - debugLog("Setting up the server def to \(serverDef)...") - let serverDef: UnsafeMutablePointer! = - TFE_GetServerDef(serverDef, status) - checkOk(status) - TFE_ContextSetServerDef(eagerContext, /*keep_alive_secs*/0, - serverDef.pointee.data, serverDef.pointee.length, status) - checkOk(status) - TF_DeleteBuffer(serverDef) - } - - let devices = TFE_ContextListDevices(eagerContext, status) - checkOk(status) - defer { TF_DeleteDeviceList(devices!) } - - let deviceCount = TF_DeviceListCount(devices!) - debugLog("There are \(deviceCount) devices.") - for deviceId in 0.. TFTensorOperation { - return TFE_Op(name, nOutputs) - } -} - -// Elements in `outputs` can come from two sources: -// a) Symbolic tensors produced by tensor ops, and added as trace graph nodes. -// b) Concrete tensors produced by host code (e.g. Tensor(1.0)). -fileprivate func finalizeTraceFunction(_ name: String) -> TraceContext { - guard let traceContext = _RuntimeConfig.traceState.context else { - fatalError("Not in tracing mode!.") - } - _RuntimeConfig.traceState = .notTracing - traceContext.finalize(traceeBasicName: name) - return traceContext -} - -private extension TensorArrayProtocol { - // The returned handles are owned by the caller. - var cTensorHandles: [CTensorHandle] { - debugLog("Getting \(self._tensorHandleCount) C handles.") - let buffer = UnsafeMutablePointer.allocate( - capacity: Int(self._tensorHandleCount)) - debugLog("Unpacking handles into buffer.") - _unpackTensorHandles(into: buffer) - let status = TF_NewStatus() - debugLog("Copying buffer content to output handles.") - var output: [CTensorHandle] = [] - for i in 0..(_copying input: C) where C.Element == CTensorHandle { - assert(Self._tensorHandleCount == input.count) - let buffer = UnsafeMutablePointer.allocate( - capacity: input.count) - let status = TF_NewStatus() - // copy input to buffer - for (i, inputTensorHandle) in input.enumerated() { - let address = buffer.advanced(by: i) - // Each tensor can be symbolic (e.g. when using this API to create a - // symbolic input instance to tracee) or concrete (e.g. when creating the - // final output of the tracee). - let newHandle = TFE_TensorHandleCopySharingTensor(inputTensorHandle, - status) - checkOk(status) - address.initialize(to: newHandle!) - } - TF_DeleteStatus(status) - self.init(_owning: buffer) - } - - init(_owning input: C) where C.Element == CTensorHandle { - assert(Self._tensorHandleCount == input.count) - let buffer = UnsafeMutablePointer.allocate( - capacity: input.count) - let status = TF_NewStatus() - // copy input to buffer - for (i, inputTensorHandle) in input.enumerated() { - let address = buffer.advanced(by: i) - address.initialize(to: inputTensorHandle) - } - TF_DeleteStatus(status) - self.init(_owning: buffer) - } -} - -// TODO: Fold this protocol into TensorArrayProtocol. -// This requires that we move concrete implementation such as -// Tensor._makeInstance() to TensorGroup.swift. -public protocol _TensorArrayProtocolEnhanced : TensorArrayProtocol { - // Create an instance based on `inputs`, which can be symbolic (e.g. when - // creating a symbolic input to tracee) or concrete (e.g. when creating a - // final output of executing the tracee). - func _makeInstance(owning inputs: C) -> Self - where C.Element == CTensorHandle -} - - -extension _TensorArrayProtocolEnhanced { - var _dtypes: [TF_DataType] { - let count = Int(_tensorHandleCount) - let buffer = - UnsafeMutableBufferPointer.allocate(capacity: count) - defer { buffer.deallocate() } - _unpackTensorHandles(into: buffer.baseAddress) - return buffer.map { TFE_TensorHandleDataType($0) } - } -} - -/// Trace `fn` with newly created tensor handles and return a trace context. -private func _trace( - with dtypes: [TF_DataType], - in fn: ([CTensorHandle]) -> [CTensorHandle] -) -> TraceContext { - debugLog(""" - Tracing over a function with \(dtypes.count) inputs. - """) - - // Verify that we are not already tracing. - internalConsistencyCheck(_RuntimeConfig.traceState.context == nil, - "Should not be in tracing mode already!") - - // Switch to tracing mode. - let traceCtx = TraceContext(dtypes: dtypes) - _RuntimeConfig.traceState = .tracing(traceCtx) - - // Handle inputs. - let inputSymbolicTensors = traceCtx.symbolicInputs.map { - TFE_NewTensorHandleFromTFOutput($0, TF_OperationOutputType($0))! - } - internalConsistencyCheck(inputSymbolicTensors.count == dtypes.count) - - // Run tracee to build the trace, adding ops to the trace graph function. - // The tracee output can contain a mixture of symbolic and concrete tensors - // (see the comment block within TraceContext.finalize()). - debugLog("Running tracee in tracing mode.") - traceCtx.tracedOutputs = fn(inputSymbolicTensors) - - debugLog("Finalizing trace graph function.") - // TAP means tensor array protocol. - let opType = "MyTraceFn_TAP" - return finalizeTraceFunction(opType) -} - -private func _graphInternal( - with state: State, - in fn: (State, Data) -> (State, Result?) -) -> (State, Data) -> (State, Result?) { - let traceContext: TraceContext = withoutActuallyEscaping(fn) { escapableFn in - let wrappedFn = { (inputs: [CTensorHandle]) -> [CTensorHandle] in - let symbolicState = state._makeInstance( - owning: inputs.dropLast(Data._typeList.count)) - let symbolicData = Data( - _copying: inputs.dropFirst(Int(state._tensorHandleCount))) - let (outputState, outputResult) = escapableFn(symbolicState, symbolicData) - - debugLog("Assembling output tensor handles.") - let outputs = - outputResult != nil - ? (outputState.cTensorHandles + outputResult!.cTensorHandles) - : outputState.cTensorHandles - return outputs - } - let dtypes = state._dtypes + Data._typeList.map { $0._cDataType } - return _trace(with: dtypes, in: wrappedFn) - } - // The result is a closure that captures and executes the trace graph - // function in the trace context. - return { (oldState: State, data: Data) -> (State, Result?) in - debugLog("Running trace function over state \(oldState) and data \(data).") - - debugLog("Getting input state tensor handles.") - let inputStateTensorHandles = oldState.cTensorHandles - var inputTensors = inputStateTensorHandles.map { - TFETensorHandle(_owning: $0) - } - debugLog("Getting input data tensor handles.") - let inputDataTensorHandles = data.cTensorHandles - inputTensors.append(contentsOf: inputDataTensorHandles.map { - TFETensorHandle(_owning: $0) - }) - - debugLog("Executing trace graph function.") - let returnValues = traceContext.execute(traceeInputs: inputTensors) - - debugLog("Creating output model instance.") - let newState = state._makeInstance(owning: returnValues.prefix( - Int(state._tensorHandleCount))) - let resultValues = returnValues.dropFirst(Int(state._tensorHandleCount)) - let result: Result? = resultValues.isEmpty ? nil : Result(_owning: resultValues) - return (newState, result) - } -} - -// TODO: rename this to `graph` when it's ready for end users. -public func _graph( - with state: State, - in fn: (State, Data) -> (State, Result) -) -> (State, Data) -> (State, Result) { - let graphFunction = _graphInternal(with: state, in: fn) - return { (state: State, data: Data) in - let result = graphFunction(state, data) - internalConsistencyCheck(result.1 != nil) - return (result.0, result.1!) - } -} - -// TODO: rename this to `graph` when it's ready for end users. -public func _graph( - with state: State, - in fn: (State, Data) -> State -) -> (State, Data) -> State { - let graphFunction: (State, Data) -> (State, Tensor?) = - withoutActuallyEscaping(fn) { escapableFn in - let wrappedFn = { - // The result argument needs to a type that conforms to TensorGroup. - // We are arbitrarily picking Tensor here. - (s: State, d: Data) -> (State, Tensor?) in - (escapableFn(s, d), nil) - } - return _graphInternal(with: state, in: wrappedFn) - } - return { (state: State, data: Data) in graphFunction(state, data).0 } -} - -/// Trace the given function `fn` and return a closure that can be used to -/// create a `TF_Function(State)` specialized for `data`. -public func _tffunc( - with state: State, - in fn: (State, Data) -> State -) -> (Data) -> (String) { - let traceContext: TraceContext = withoutActuallyEscaping(fn) { escapableFn in - let wrappedFn = { (inputs: [CTensorHandle]) -> [CTensorHandle] in - let symbolicState = state._makeInstance( - owning: inputs.dropLast(Data._typeList.count)) - let symbolicData = Data( - _copying: inputs.dropFirst(Int(state._tensorHandleCount))) - let outputState = escapableFn(symbolicState, symbolicData) - return outputState.cTensorHandles - } - let dtypes = state._dtypes + Data._typeList.map { $0._cDataType } - return _trace(with: dtypes, in: wrappedFn) - } - return { - data in traceContext.specializeTFFunction(with: data.cTensorHandles) - } -} - -// Trace the given function to generate a TF graph and return a closure -// that can be used to launch the graph. -public func _graph( - _ fn: (In) -> Out, useXLA: Bool = false -) -> (In) -> Out { - let traceContext: TraceContext = withoutActuallyEscaping(fn) { escapableFn in - let wrappedFn = { (inputs: [CTensorHandle]) -> [CTensorHandle] in - let buffer = UnsafeMutablePointer.allocate( - capacity: Int(inputs.count)) - var ptr = buffer - for input in inputs { - ptr.initialize(to: input) - ptr = ptr.advanced(by: 1) - } - let symbolicIn = In(_owning: buffer) - let symbolicOut = escapableFn(symbolicIn) - return symbolicOut.cTensorHandles - } - let dtypes = In._typeList.map { $0._cDataType } - return _trace(with: dtypes, in: wrappedFn) - } - // The result is a closure that captures and executes the trace graph - // function in the trace context. - return { (input: In) -> (Out) in - debugLog("Running trace function over input \(input).") - - debugLog("Getting input state tensor handles.") - let inputStateTensorHandles = input.cTensorHandles - let inputTensors = inputStateTensorHandles.map { - TFETensorHandle(_owning: $0) - } - debugLog("Executing trace graph function.") - let returnValues = traceContext.execute( - traceeInputs: inputTensors, useXLA: useXLA) - - debugLog("Creating output model instance.") - return Out(_owning: returnValues) - } -} - -/// Trace the given function and return the name of the corresponding -/// `TF_Function: In -> Out` that was created. -public func _tffunc( - _ fn: (In) -> Out -) -> String { - let traceContext: TraceContext = withoutActuallyEscaping(fn) { escapableFn in - let wrappedFn = { - (inputs: [CTensorHandle]) -> [CTensorHandle] in - let buffer = UnsafeMutablePointer.allocate( - capacity: Int(inputs.count)) - var ptr = buffer - for input in inputs { - ptr.initialize(to: input) - ptr = ptr.advanced(by: 1) - } - let symbolicIn = In(_owning: buffer) - let symbolicOut = escapableFn(symbolicIn) - return symbolicOut.cTensorHandles - } - - let dtypes = In._typeList.map { $0._cDataType } - return _trace(with: dtypes, in: wrappedFn) - } - return traceContext.specializeTFFunction(with: []) -} - -internal extension _ExecutionContext { - /// Returns a valid TensorFlow device name, which corresponds to the - /// closest enclosing call to one of the overloads of withDevice. - /// A return value of `nil` indicates the absence of a withDevice call on - /// the call stack or the presence of an immediately enclosing - /// `withDefaultDevice(perform)` call. - var currentDeviceName: String? { - return _ThreadLocalState.local._currentDevice - } - - /// See documentation for the top-level `withDevice(_:_:perform)`. - func withDevice(_ kind: DeviceKind, _ index: UInt = 0, - perform body: () throws -> R) rethrows -> R { - let name: String - switch kind { - case .cpu: - name = "/job:localhost/replica:0/task:0/device:CPU:\(index)" - case .gpu: - name = "/job:localhost/replica:0/task:0/device:GPU:\(index)" - case .tpu: - // According to server def generated when you set - // SWIFT_TENSORFLOW_SERVER_ADDRESS, the TPUs will all be on task 1. - name = "/job:localhost/replica:0/task:1/device:TPU:\(index)" - } - return try withDevice(named: name, perform: body) - } - - /// See documentation for the top-level `withDevice(named:perform)`. - func withDevice(named name: String, - perform body: () throws -> R) rethrows -> R { - guard deviceNames.contains(name) else { - fatalError("Device \(name) not found") - } - _ThreadLocalState.local.pushDevice(name) - let result = try body() - _ThreadLocalState.local.popDevice() - return result - } - - /// See documentation for the top-level `withDefaultDevice(perform)`. - func withDefaultDevice(perform body: () throws -> R) rethrows -> R { - _ThreadLocalState.local.pushDevice(nil) - let result = try body() - _ThreadLocalState.local.popDevice() - return result - } -} - -internal extension _ExecutionContext { - /// Synchronously execute the body, preventing asynchronous computation from - /// corrupting the context data. - private func sync( - execute body: () throws -> Result - ) rethrows -> Result { - let lockStatus = pthread_mutex_lock(&mutex) - internalConsistencyCheck(lockStatus == 0) - defer { - let unlockStatus = pthread_mutex_unlock(&mutex) - internalConsistencyCheck(unlockStatus == 0) - // Create a cancellation point. - pthread_testcancel() - } - return try body() - } -} - -@usableFromInline -internal func dumpTensorContent( - _ inputTensor: CTensorHandle, _: Scalar.Type -) { - assert(TFE_TensorHandleIsConcrete(inputTensor) != 0) - - let array = ShapedArray(cTensorHandle: inputTensor) - debugLog("Rank is \(array.rank), shape is \(array.shape).") - debugLog(""" - The content of the \(array.scalars.count) scalars are: \ - \(array.scalars). - """) -} - -@usableFromInline -internal func dumpCTensorHandleContent( - _ idx: Int, - _ inputTensorHandle: CTensorHandle) { - if TFE_TensorHandleIsConcrete(inputTensorHandle) == 0 { - debugLog("Skip dumpping a symbolic tensor handle.") - return - } - - let dType: TF_DataType = TFE_TensorHandleDataType(inputTensorHandle) - debugLog("Tensor \(idx) has TF data type \(dType).") - switch dType { - case TF_UINT8: dumpTensorContent(inputTensorHandle, UInt8.self) - case TF_INT8: dumpTensorContent(inputTensorHandle, Int8.self) - case TF_UINT16: dumpTensorContent(inputTensorHandle, UInt16.self) - case TF_INT16: dumpTensorContent(inputTensorHandle, Int16.self) - case TF_UINT32: dumpTensorContent(inputTensorHandle, UInt32.self) - case TF_INT32: dumpTensorContent(inputTensorHandle, Int32.self) - case TF_UINT64: dumpTensorContent(inputTensorHandle, UInt64.self) - case TF_INT64: dumpTensorContent(inputTensorHandle, Int64.self) - case TF_FLOAT: dumpTensorContent(inputTensorHandle, Float.self) - case TF_DOUBLE: dumpTensorContent(inputTensorHandle, Double.self) - case TF_BOOL: dumpTensorContent(inputTensorHandle, Bool.self) - // TODO: Handle `TF_BFloat16`? BFloat16 does not have a host-side - // representation and cannot be printed directly. Consider calling into TF - // runtime. - default: fatalError("Unsupported dtype \(dType)") - } -} - -@usableFromInline -@_cdecl("_swift_tfc_EagerExecute") -func _TFCEagerExecute(_ op: CTFEOp, - _ retvals: UnsafeMutablePointer, - _ retvalCount: UnsafeMutablePointer, - _ status: CTFStatus) { - if _RuntimeConfig.printsDebugLog { - debugLog("Calling _TFCEagerExecute() over: ") - if let value = getenv("TF_CPP_MIN_LOG_LEVEL"), - String(cString: value) == "0" { - TFE_OpPrintDebugString(op) - } else { - debugLog("[Run with TF_CPP_MIN_LOG_LEVEL=0 to have TFEOps printed out]") - } - } - if let traceContext = _RuntimeConfig.traceState.context { - // convert this eager op into a trace graph node - debugLog("Adding eager op \(op) to trace graph.") - traceContext.addEagerOpToGraph(op, retvals, retvalCount, status) - checkOk(status) - } else { - debugLog("Executing eager op \(op).") - TFE_Execute(op, retvals, retvalCount, status) - } -} - -//===----------------------------------------------------------------------===// -// - MARK: Dynamic compilation (per-op dispatch) entrypoints -//===----------------------------------------------------------------------===// - -@usableFromInline -@_cdecl("_swift_tfc_GetGlobalEagerContext") -func _TFCGetGlobalEagerContext() -> CTFEContext { - debugLog("Calling _GetGlobalEagerContext()") - return _ExecutionContext.global.eagerContext -} - -// Some of the functions are marked with @silgen_name instead of @_cdecl, -// because their input/output data types are not C-compatible -// (e.g. AnyTensorHandle). - -/// Adds `handle` as an input to `op`. -@usableFromInline -@_silgen_name("_swift_tfc_OpAddInputFromTensorHandle") -func _TFCOpAddInputFromTensorHandle(_ op: CTFEOp, - _ handle: _AnyTensorHandle, - _ status: CTFStatus) { - TFE_OpAddInput(op, handle._cTensorHandle, status) -} - -/// Adds `t` as an input or inputs to `op`. Returns the number of inputs added. -@usableFromInline -@_silgen_name("_swift_tfc_OpAddInputFromTensorGroup") -func _TFCOpAddInputFromTensorGroup( - _ op: CTFEOp, _ t: T, _ status: CTFStatus -) -> Int32 { - let count = t._tensorHandleCount - let buffer = - UnsafeMutableBufferPointer.allocate(capacity: Int(count)) - defer { buffer.deallocate() } - t._unpackTensorHandles(into: buffer.baseAddress) - for handle in buffer { - TFE_OpAddInput(op, handle, status) - guard TF_GetCode(status) == TF_OK else { - return 0 - } - } - return count -} - -/// Special protocol for calling tensorflow operations that take heterogeneous -/// arrays as input. -public protocol AnyTensor { - var _rawTensorHandle: CTensorHandle { get } - var _tensorFlowDataType: TensorDataType { get } -} - -extension Tensor : AnyTensor { - public var _rawTensorHandle: CTensorHandle { return handle._cTensorHandle } - public var _tensorFlowDataType: TensorDataType { return Scalar.tensorFlowDataType } -} - -@usableFromInline -func _TFCOpAddInputFromAnyTensors( - _ op: CTFEOp, _ tensors: [AnyTensor], _ status: CTFStatus -) { - for tensor in tensors { - let handle = tensor._rawTensorHandle - TFE_OpAddInput(op, handle, status) - checkOk(status) - } -} - -// _TFCOpSetAttr*Array functions are wrappers around TFE_OpSetAttr*List -// functions. The wrappers handle converting the Swift Stdlib Array values -// into buffers that TFE_OpSetAttr*List functions can read. - -@usableFromInline -@_silgen_name("_swift_tfc_OpSetAttrTypeArray") -func _TFCOpSetAttrTypeArray(_ op: CTFEOp, - _ attrName: UnsafePointer, - _ value: Array) { - value.withUnsafeBufferPointer { buffer in - buffer.withMemoryRebound(to: TF_DataType.self) { reboundBuffer in - TFE_OpSetAttrTypeList(op, attrName, reboundBuffer.baseAddress, - Int32(reboundBuffer.count)) - } - } -} - -/// Given dimensions and ranks in the form described below, makes the -/// appropriate call to `TFE_OpSetAttrShapeList(op, attrName, ..., status)`. -/// -/// - Parameters -/// - flattenedDims: all the shapes' dimensions concatenated together in -/// order -/// - ranks: all the shapes' ranks (-1 denotes unknown rank) -fileprivate func setAttrShapeList( - op: CTFEOp, attrName: UnsafePointer, flattenedDims: Array, - ranks: Array, status: CTFStatus -) { - flattenedDims.withUnsafeBufferPointer { flattenedDimsBuffer in - var dimsPtr: UnsafePointer? = flattenedDimsBuffer.baseAddress - var dims: [UnsafePointer?] = [] - for rank in ranks { - dims.append(dimsPtr) - if rank >= 0 { - dimsPtr = dimsPtr.map { $0.advanced(by: Int(rank)) } - } - } - dims.withUnsafeMutableBufferPointer { dimsBuffer in - ranks.withUnsafeBufferPointer { ranksBuffer in - TFE_OpSetAttrShapeList(op, attrName, dimsBuffer.baseAddress, - ranksBuffer.baseAddress, - Int32(ranksBuffer.count), status) - } - } - } -} - -/// Stack of devices that models nested calls to withDevice/withDefaultDevice. -/// Devices are represented by their names in TensorFlow notation. -/// See documentation for `withDevice(named:perform:)` to learn about device names. -/// -/// All TensorFlow operations will be put on the topmost device on the stack. -/// When the stack is empty or the topmost device is `nil`, that allows -/// TensorFlow to place operations on any device that it sees fit. -@usableFromInline -class _ThreadLocalState { - var deviceScopes: [String?] = [] - - private static let key: pthread_key_t = { - var key = pthread_key_t() - pthread_key_create(&key) { -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let _: AnyObject = Unmanaged.fromOpaque($0).takeRetainedValue() -#else - let _: AnyObject = Unmanaged.fromOpaque($0!).takeRetainedValue() -#endif - } - return key - }() - - var _currentDevice: String? { - return deviceScopes.last ?? nil - } - - @usableFromInline - func pushDevice(_ device: String?) { - deviceScopes.append(device) - } - - @usableFromInline - func popDevice() { - internalConsistencyCheck(deviceScopes.popLast() != nil) - } - - @usableFromInline - static var local: _ThreadLocalState { - if let state = pthread_getspecific(key) { - return Unmanaged.fromOpaque(state).takeUnretainedValue() - } - let state = _ThreadLocalState() - pthread_setspecific(key, Unmanaged.passRetained(state).toOpaque()) - return state - } -} - -@usableFromInline -@_cdecl("_swift_tfc_OpSetDeviceFromScope") -func _TFCOpSetDeviceFromScope(_ op: CTFEOp, _ status: CTFStatus) { - if let deviceName = _ExecutionContext.global.currentDeviceName { - TFE_OpSetDevice(op, deviceName, status) - } -} diff --git a/stdlib/public/TensorFlow/TensorHandle.swift b/stdlib/public/TensorFlow/TensorHandle.swift index 352b0ae2ecb5a..3fe8ca034dd87 100644 --- a/stdlib/public/TensorFlow/TensorHandle.swift +++ b/stdlib/public/TensorFlow/TensorHandle.swift @@ -164,8 +164,8 @@ internal extension ShapedArray where Scalar : _TensorFlowDataTypeCompatible { public struct ResourceHandle { let handle: _AnyTensorHandle - @usableFromInline - var _cTensorHandle: CTensorHandle { handle._cTensorHandle } + @inlinable + public var _cTensorHandle: CTensorHandle { handle._cTensorHandle } @usableFromInline init(owning cTensorHandle: CTensorHandle) { @@ -178,8 +178,8 @@ public struct ResourceHandle { public struct VariantHandle { let handle: _AnyTensorHandle - @usableFromInline - var _cTensorHandle: CTensorHandle { handle._cTensorHandle } + @inlinable + public var _cTensorHandle: CTensorHandle { handle._cTensorHandle } @usableFromInline init(owning cTensorHandle: CTensorHandle) { diff --git a/stdlib/public/TensorFlow/Utilities.swift b/stdlib/public/TensorFlow/Utilities.swift deleted file mode 100644 index 59e3c11709a7e..0000000000000 --- a/stdlib/public/TensorFlow/Utilities.swift +++ /dev/null @@ -1,153 +0,0 @@ -//===-- Utilities.swift ---------------------------------------*- swift -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This file defines utility functions and common type aliases. -// -//===----------------------------------------------------------------------===// - -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) -import Darwin -#else -import Glibc -#endif -import CTensorFlow - -//===----------------------------------------------------------------------===// -// Runtime checkers -//===----------------------------------------------------------------------===// - -/// These checks run in both debug and release modes (while assert() only runs -/// in debug mode), to help shake out more bugs and facilitate debugging in the -/// early project phases. It can be replaced with plain assert() later, when we -/// have a more mature code base. -@usableFromInline -func internalConsistencyCheck( - _ predicate: Bool, - _ errMessage: String = "TF runtime assertion failure", - file: StaticString = #file, - line: UInt = #line -) { - guard predicate else { - fatalError(errMessage, file: file, line: line) - } -} - -@usableFromInline -func checkOk(_ s: CTFStatus?, file: StaticString = #file, line: UInt = #line) { - internalConsistencyCheck(TF_GetCode(s) == TF_OK, - String(cString: TF_Message(s)), - file: file, line: line) -} - -//===----------------------------------------------------------------------===// -// Type aliases -//===----------------------------------------------------------------------===// - -// Before assigning a C pointer to one of the pointer type aliases below, caller -// should check that the pointer is not NULL. - -/// The `TF_Session *` type. -@usableFromInline typealias CTFSession = OpaquePointer - -/// The `TF_Status *` type. -@usableFromInline typealias CTFStatus = OpaquePointer - -/// The `TF_Graph*` type. -@usableFromInline typealias CTFGraph = OpaquePointer - -/// The `TF_Function*` type. -@usableFromInline typealias CTFFunction = OpaquePointer - -/// The `TF_Tensor *` type. -@usableFromInline typealias CTensor = OpaquePointer - -/// The `TF_TensorHandle *` type. -/// -/// - Note: This is public so that compiler generated code can read/write tensor -/// handles when calling runtime APIs. -public typealias CTensorHandle = OpaquePointer - -/// The `TFE_Context *` type. -public typealias CTFEContext = OpaquePointer - -/// The `TFE_Op *` type. -@usableFromInline typealias CTFEOp = OpaquePointer - -/// The `TF_OperationDescription *` type. -@usableFromInline typealias CTFOperationDescription = OpaquePointer - -/// The `TFE_TraceContext *` type. -@usableFromInline typealias CTFETraceContext = OpaquePointer - -//===----------------------------------------------------------------------===// -// Logging -//===----------------------------------------------------------------------===// - -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) -let stderr = __stderrp -let stdout = __stdoutp -#endif - -/// Log to standard error. -func logToStderr(_ message: StaticString) { - message.utf8Start - .withMemoryRebound(to: Int8.self, capacity: message.utf8CodeUnitCount) { - _ = fputs($0, stderr) - } -} - -/// Log to standard error. -func logToStderr(_ message: String) { - _ = fputs(message, stderr) -} - -@usableFromInline -func debugLog(_ message: @autoclosure () -> String, - file: StaticString = #file, - line: UInt = #line) { - if _RuntimeConfig.printsDebugLog { - print("[\(file):\(line)] \(message())") - // This helps dump more log before a crash. - fflush(stdout) - } -} - -//===----------------------------------------------------------------------===// -// File writing -//===----------------------------------------------------------------------===// - -/// Given the address of a `TF_Buffer` and a file path, write the buffer's -/// contents to the file. -func writeContents(of buffer: UnsafePointer, - toFile path: String) { - let fp = fopen(path, "w+") - fwrite(buffer.pointee.data, /*size*/ 1, /*count*/ buffer.pointee.length, fp) - fclose(fp) -} - -//===----------------------------------------------------------------------===// -// Unit test utilities -//===----------------------------------------------------------------------===// - -// TODO: Consider revising the call sites where this is necessary to only need -// UnsafeMutablePointer to optional when it is the actual c-api call site. -extension UnsafeMutablePointer where Pointee == CTensorHandle? { - @usableFromInline - init(_ other: UnsafeMutablePointer) { - self.init(other._rawValue) - } - @usableFromInline - init?(_ other: UnsafeMutablePointer?) { - guard let unwrapped = other else { return nil } - self.init(unwrapped) - } -} diff --git a/utils/build-script b/utils/build-script index 56e4e46362537..58a74ca11abe4 100755 --- a/utils/build-script +++ b/utils/build-script @@ -393,12 +393,6 @@ class BuildScriptInvocation(object): "--tensorflow-host-include-dir (to use custom TensorFlow " "headers/libraries); please pick one") - if args.tensorflow_swift_bindings is not None and \ - not os.path.isdir(args.tensorflow_swift_bindings): - diagnostics.fatal( - "invalid repository specified for --tensorflow-swift-bindings " - "(was '{}')".format(args.tensorflow_swift_bindings)) - if args.tensorflow_swift_apis is not None: if not os.path.isdir(args.tensorflow_swift_apis): diagnostics.fatal( diff --git a/utils/build-script-impl b/utils/build-script-impl index 3dfbd186df11c..9bc1d1ea3f1bc 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -296,7 +296,6 @@ KNOWN_SETTINGS=( tensorflow-host-include-dir "" "Path to host TensorFlow headers" tensorflow-target-include-dir "" "Path to target Tensorflow headers" tensorflow-target-lib-dir "" "Path to target TensorFlow libraries" - tensorflow-swift-bindings "" "Path to TensorFlow Swift bindings repository" tensorflow-swift-apis "" "Path to TensorFlow deep learning library repository" ) @@ -1236,7 +1235,6 @@ LIBCXX_SOURCE_DIR="${WORKSPACE}/libcxx" PLAYGROUNDSUPPORT_SOURCE_DIR="${WORKSPACE}/swift-xcode-playground-support" # SWIFT_ENABLE_TENSORFLOW TENSORFLOW_SOURCE_DIR="${WORKSPACE}/tensorflow" -TENSORFLOW_SWIFT_BINDINGS_DIR="${WORKSPACE}/tensorflow-swift-bindings" TENSORFLOW_SWIFT_APIS_DIR="${WORKSPACE}/tensorflow-swift-apis" if [[ ! "${SKIP_BUILD_PLAYGROUNDSUPPORT}" && ! -d ${PLAYGROUNDSUPPORT_SOURCE_DIR} ]]; then diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/driver_arguments.py index 2db8a5d751bb1..0f1872d19ff01 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/driver_arguments.py @@ -1040,9 +1040,6 @@ def create_argument_parser(): default=None, help='Path to a directory containing TensorFlow headers. ' 'Used for linking Swift programs.') - option('--tensorflow-swift-bindings', store_path, - default=None, - help='Path to a TensorFlow Swift bindings repository.') option('--tensorflow-swift-apis', store_path, default=None, help='Path to a TensorFlow deep learning library repository.') diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index bc63cef53c291..953427fbe021a 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -607,7 +607,6 @@ class IgnoreOption(_BaseOption): PathOption('--tensorflow-host-include-dir'), PathOption('--tensorflow-target-lib-dir'), PathOption('--tensorflow-target-include-dir'), - PathOption('--tensorflow-swift-bindings'), PathOption('--tensorflow-swift-apis'), PathOption('--host-bazel'), AppendOption('--tensorflow-bazel-options'), diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index da8be5dddc436..8ba2671d70c0b 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -36,8 +36,6 @@ "remote": { "id": "ninja-build/ninja" } }, "tensorflow": { "remote": { "id": "tensorflow/tensorflow" } }, - "tensorflow-swift-bindings": { - "remote": { "id": "tensorflow/swift-bindings" } }, "tensorflow-swift-apis": { "remote": { "id": "tensorflow/swift-apis" } }, "icu": { @@ -351,7 +349,6 @@ "clang-tools-extra": "swift-DEVELOPMENT-SNAPSHOT-2019-05-20-a", "libcxx": "swift-DEVELOPMENT-SNAPSHOT-2019-05-20-a", "tensorflow": "ebc41609e27dcf0998d8970e77a2e1f53e13ac86", - "tensorflow-swift-bindings": "6b80eaec40d4ce30f447804fe3c510f435c508a9", "tensorflow-swift-apis": "ca8ac09285b0aaa7c6bbd08c375b1ba2b9e259e9", "indexstore-db": "swift-DEVELOPMENT-SNAPSHOT-2019-05-20-a", "sourcekit-lsp": "swift-DEVELOPMENT-SNAPSHOT-2019-05-20-a"